639 const QFixedPoint relativePoint(point.x - fd->position.x, point.y - fd->position.y);
641 QTextFrame *rootFrame = docPrivate->rootFrame();
643 qCDebug(lcHit) <<
"checking frame" << frame->firstPosition() <<
"point=" << point.toPointF()
644 <<
"position" << fd->position.toPointF() <<
"size" << fd->size.toSizeF();
645 if (frame != rootFrame) {
646 if (relativePoint.y < 0 || relativePoint.x < 0) {
647 *position = frame->firstPosition() - 1;
648 qCDebug(lcHit) <<
"before pos=" << *position;
650 }
else if (relativePoint.y > fd->size.height || relativePoint.x > fd->size.width) {
651 *position = frame->lastPosition() + 1;
652 qCDebug(lcHit) <<
"after pos=" << *position;
658 *position = frame->firstPosition() - 1;
662 if (QTextTable *table = qobject_cast<QTextTable *>(frame)) {
663 const int rows = table->rows();
664 const int columns = table->columns();
667 if (!td->childFrameMap.isEmpty()) {
668 for (
int r = 0; r < rows; ++r) {
669 for (
int c = 0; c < columns; ++c) {
670 QTextTableCell cell = table->cellAt(r, c);
671 if (cell.row() != r || cell.column() != c)
674 QRectF cellRect = td->cellRect(cell);
675 const QFixedPoint cellPos = QFixedPoint::fromPointF(cellRect.topLeft());
676 const QFixedPoint pointInCell = relativePoint - cellPos;
678 const QList<QTextFrame *> childFrames = td->childFrameMap.values(r + c * rows);
679 for (
int i = 0; i < childFrames.size(); ++i) {
680 QTextFrame *child = childFrames.at(i);
681 if (isFrameFromInlineObject(child)
682 && child->frameFormat().position() != QTextFrameFormat::InFlow
683 && hitTest(child, pointInCell, position, l, accuracy) == PointExact)
692 return hitTest(table, relativePoint, position, l, accuracy);
695 const QList<QTextFrame *> childFrames = frame->childFrames();
696 for (
int i = 0; i < childFrames.size(); ++i) {
697 QTextFrame *child = childFrames.at(i);
698 if (isFrameFromInlineObject(child)
699 && child->frameFormat().position() != QTextFrameFormat::InFlow
700 && hitTest(child, relativePoint, position, l, accuracy) == PointExact)
706 QTextFrame::Iterator it = frame->begin();
708 if (frame == rootFrame) {
709 it = frameIteratorForYPosition(relativePoint.y);
711 Q_ASSERT(it.parentFrame() == frame);
714 if (it.currentFrame())
715 *position = it.currentFrame()->firstPosition();
717 *position = it.currentBlock().position();
719 return hitTest(it, PointBefore, relativePoint, position, l, accuracy);
1044 const QAbstractTextDocumentLayout::PaintContext &context,
1045 QTextFrame *frame)
const
1056 const QPointF off = QPointF(QPointF(offset + fd->position.toPointF()).toPoint());
1058 if (context.clip.isValid()
1059 && (off.y() > context.clip.bottom() || off.y() + fd->size.height.toReal() < context.clip.top()
1060 || off.x() > context.clip.right() || off.x() + fd->size.width.toReal() < context.clip.left()))
1063 qCDebug(lcDraw) <<
"drawFrame" << frame->firstPosition() <<
"--" << frame->lastPosition() <<
"at" << offset;
1067 QTextBlock cursorBlockNeedingRepaint;
1068 QPointF offsetOfRepaintedCursorBlock = off;
1070 QTextTable *table = qobject_cast<QTextTable *>(frame);
1071 const QRectF frameRect(off, fd->size.toSizeF());
1074 const int rows = table->rows();
1075 const int columns = table->columns();
1078 QVarLengthArray<
int> selectedTableCells(context.selections.size() * 4);
1079 for (
int i = 0; i < context.selections.size(); ++i) {
1080 const QAbstractTextDocumentLayout::Selection &s = context.selections.at(i);
1081 int row_start = -1, col_start = -1, num_rows = -1, num_cols = -1;
1083 if (s.cursor.currentTable() == table)
1084 s.cursor.selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
1086 selectedTableCells[i * 4] = row_start;
1087 selectedTableCells[i * 4 + 1] = col_start;
1088 selectedTableCells[i * 4 + 2] = num_rows;
1089 selectedTableCells[i * 4 + 3] = num_cols;
1092 QFixed pageHeight = QFixed::fromReal(document->pageSize().height());
1093 if (pageHeight <= 0)
1094 pageHeight = QFIXED_MAX;
1096 QFixed absYPos = td->position.y;
1097 QTextFrame *parentFrame = table->parentFrame();
1098 while (parentFrame) {
1099 absYPos +=
data(parentFrame
)->position.y;
1100 parentFrame = parentFrame->parentFrame();
1102 const int tableStartPage = (absYPos / pageHeight).truncate();
1103 const int tableEndPage = ((absYPos + td->size.height) / pageHeight).truncate();
1111 const int headerRowCount = qMin(table->format().headerRowCount(), rows - 1);
1112 int page = tableStartPage + 1;
1113 while (page <= tableEndPage) {
1114 const QFixed pageTop = page * pageHeight + td->effectiveTopMargin + td->cellSpacing + td->border;
1115 const qreal headerOffset = (pageTop - td->rowPositions.at(0)).toReal();
1116 for (
int r = 0; r < headerRowCount; ++r) {
1117 for (
int c = 0; c < columns; ++c) {
1118 QTextTableCell cell = table->cellAt(r, c);
1119 QAbstractTextDocumentLayout::PaintContext cell_context = context;
1120 adjustContextSelectionsForCell(cell_context, cell, r, c, selectedTableCells.data());
1121 QRectF cellRect = td->cellRect(cell);
1123 cellRect.translate(off.x(), headerOffset);
1124 if (cellClipTest(table, td, cell_context, cell, cellRect))
1127 drawTableCell(cellRect, painter, cell_context, table, td, r, c, &cursorBlockNeedingRepaint,
1128 &offsetOfRepaintedCursorBlock);
1137 if (context.clip.isValid()) {
1138 auto rowIt = std::lower_bound(td->rowPositions.constBegin(), td->rowPositions.constEnd(), QFixed::fromReal(context.clip.top() - off.y()));
1139 if (rowIt != td->rowPositions.constEnd() && rowIt != td->rowPositions.constBegin()) {
1141 firstRow = rowIt - td->rowPositions.constBegin();
1144 rowIt = std::upper_bound(td->rowPositions.constBegin(), td->rowPositions.constEnd(), QFixed::fromReal(context.clip.bottom() - off.y()));
1145 if (rowIt != td->rowPositions.constEnd()) {
1147 lastRow = rowIt - td->rowPositions.constBegin();
1151 for (
int c = 0; c < columns; ++c) {
1152 QTextTableCell cell = table->cellAt(firstRow, c);
1153 firstRow = qMin(firstRow, cell.row());
1156 for (
int r = firstRow; r < lastRow; ++r) {
1157 for (
int c = 0; c < columns; ++c) {
1158 QTextTableCell cell = table->cellAt(r, c);
1159 QAbstractTextDocumentLayout::PaintContext cell_context = context;
1160 adjustContextSelectionsForCell(cell_context, cell, r, c, selectedTableCells.data());
1161 QRectF cellRect = td->cellRect(cell);
1163 cellRect.translate(off);
1164 if (cellClipTest(table, td, cell_context, cell, cellRect))
1167 drawTableCell(cellRect, painter, cell_context, table, td, r, c, &cursorBlockNeedingRepaint,
1168 &offsetOfRepaintedCursorBlock);
1175 QTextFrame::Iterator it = frame->begin();
1177 if (frame == docPrivate->rootFrame())
1178 it = frameIteratorForYPosition(QFixed::fromReal(context.clip.top()));
1180 QList<QTextFrame *> floats;
1181 const int numFloats = fd->floats.size();
1182 floats.reserve(numFloats);
1183 for (
int i = 0; i < numFloats; ++i)
1184 floats.append(fd->floats.at(i));
1186 drawFlow(off, painter, context, it, floats, &cursorBlockNeedingRepaint);
1189 if (cursorBlockNeedingRepaint.isValid()) {
1190 const QPen oldPen = painter->pen();
1191 painter->setPen(context.palette.color(QPalette::Text));
1192 const int cursorPos = context.cursorPosition - cursorBlockNeedingRepaint.position();
1193 cursorBlockNeedingRepaint.layout()->drawCursor(painter, offsetOfRepaintedCursorBlock,
1195 painter->setPen(oldPen);
1563 QTextTable *table,
const QTextTableData *td,
const QTextTableCell &cell,
1564 const QRectF &borderRect,
QCss::
Edge edge,
1565 int forceHeaderRow,
bool adjustTopAnchor,
bool adjustBottomAnchor,
1566 bool ignoreEdgesAbove)
1573 if (edgeData.width == 0)
1576 QTextTableCellFormat fmt = edgeData.cell.format().toTableCellFormat();
1577 QTextFrameFormat::BorderStyle borderStyle = QTextFrameFormat::BorderStyle_None;
1581 borderStyle = table->format().borderStyle();
1582 brush = table->format().borderBrush();
1585 switch (edgeData
.edge) {
1587 brush = fmt.topBorderBrush();
1588 borderStyle = fmt.topBorderStyle();
1591 brush = fmt.bottomBorderBrush();
1592 borderStyle = fmt.bottomBorderStyle();
1595 brush = fmt.leftBorderBrush();
1596 borderStyle = fmt.leftBorderStyle();
1599 brush = fmt.rightBorderBrush();
1600 borderStyle = fmt.rightBorderStyle();
1608 if (borderStyle == QTextFrameFormat::BorderStyle_None)
1612 if (brush.style() == Qt::NoBrush)
1615 QTextTableCell cellOrHeader = cell;
1616 if (forceHeaderRow != -1)
1617 cellOrHeader = table->cellAt(forceHeaderRow, cell.column());
1622 wv = p->scaleToDevice(edgeData.width);
1623 p1 = borderRect.topLeft()
1625 p2 = borderRect.topRight()
1626 + QPointF(-qCeil(prioritizedEdgeAnchorOffset(p, table, td, cell, edgeData, QCss::RightEdge,
true, ignoreEdgesAbove)), 0);
1629 wv = p->scaleToDevice(edgeData.width);
1630 p1 = borderRect.bottomLeft()
1632 p2 = borderRect.bottomRight()
1633 + QPointF(-qCeil(prioritizedEdgeAnchorOffset(p, table, td, cell, edgeData, QCss::RightEdge,
true,
false)), -wv);
1636 wh = p->scaleToDevice(edgeData.width);
1637 p1 = borderRect.topLeft()
1638 + QPointF(0, adjustTopAnchor ? qFloor(prioritizedEdgeAnchorOffset(p, table, td, cellOrHeader, edgeData,
1640 false, ignoreEdgesAbove))
1642 p2 = borderRect.bottomLeft()
1643 + QPointF(0, adjustBottomAnchor ? -qCeil(prioritizedEdgeAnchorOffset(p, table, td, cell, edgeData, QCss::BottomEdge,
true,
false))
1647 wh = p->scaleToDevice(edgeData.width);
1648 p1 = borderRect.topRight()
1649 + QPointF(-wh, adjustTopAnchor ? qFloor(prioritizedEdgeAnchorOffset(p, table, td, cellOrHeader, edgeData,
1651 false, ignoreEdgesAbove))
1653 p2 = borderRect.bottomRight()
1654 + QPointF(-wh, adjustBottomAnchor ? -qCeil(prioritizedEdgeAnchorOffset(p, table, td, cell, edgeData, QCss::BottomEdge,
true,
false))
1667 offset = QPointF(0, -qCeil(collapseOffset(p, edgeData)));
1669 case QCss::BottomEdge:
1670 offset = QPointF(0, qFloor(collapseOffset(p, edgeData)));
1672 case QCss::LeftEdge:
1673 offset = QPointF(-qCeil(collapseOffset(p, edgeData)), 0);
1675 case QCss::RightEdge:
1676 offset = QPointF(qFloor(collapseOffset(p, edgeData)), 0);
1687#ifdef COLLAPSE_DEBUG
1688 QColor c = brush.color();
1693 qDrawEdge(painter, p1.x(), p1.y(), p2.x() + wh, p2.y() + wv, 0, 0, edge, cssStyle, brush);
1699 const QTextTableCell &cell)
const
1701#ifndef QT_NO_CSSPARSER
1702 qreal topMarginAfterPageBreak = (td->effectiveTopMargin + td->cellSpacing + td->border).toReal();
1703 qreal bottomMargin = (td->effectiveBottomMargin + td->cellSpacing + td->border).toReal();
1705 const int headerRowCount = qMin(table->format().headerRowCount(), table->rows() - 1);
1706 if (headerRowCount > 0 && cell.row() >= headerRowCount)
1707 topMarginAfterPageBreak += td->headerHeight.toReal();
1709 BorderPaginator paginator(document, cellRect, topMarginAfterPageBreak, bottomMargin, 0);
1711 bool turn_off_antialiasing = !(painter->renderHints() & QPainter::Antialiasing);
1712 painter->setRenderHint(QPainter::Antialiasing);
1716 const QRectF clipped = paginator.clipRect(page);
1717 if (!clipped.isValid())
1720 const qreal offset = cellRect.top() - td->rowPositions.at(cell.row()).toReal();
1721 const int lastHeaderRow = table->format().headerRowCount() - 1;
1722 const bool tableHasHeader = table->format().headerRowCount() > 0;
1723 const bool isHeaderRow = cell.row() < table->format().headerRowCount();
1724 const bool isFirstRow = cell.row() == lastHeaderRow + 1;
1725 const bool isLastRow = cell.row() + cell.rowSpan() >= table->rows();
1726 const bool previousRowOnPreviousPage = !isFirstRow
1728 && BorderPaginator(document,
1729 td->cellRect(adjacentCell(table, cell, QCss::TopEdge)).translated(0, offset),
1730 topMarginAfterPageBreak,
1732 0).bottomPage < page;
1733 const bool nextRowOnNextPage = !isLastRow
1734 && BorderPaginator(document,
1735 td->cellRect(adjacentCell(table, cell, QCss::BottomEdge)).translated(0, offset),
1736 topMarginAfterPageBreak,
1739 const bool rowStartsOnPage = page == paginator
.topPage;
1740 const bool rowEndsOnPage = page == paginator
.bottomPage;
1741 const bool rowStartsOnPageTop = !tableHasHeader
1743 && previousRowOnPreviousPage;
1744 const bool rowStartsOnPageBelowHeader = tableHasHeader
1746 && previousRowOnPreviousPage;
1749 ? !isHeaderRow && (!rowStartsOnPage || rowStartsOnPageBelowHeader)
1752 ? !isHeaderRow && (!rowEndsOnPage || nextRowOnNextPage)
1755 ? !tableHasHeader && !rowStartsOnPage
1757 const bool doNotAdjustBottomAnchor = suppressBottomBorder;
1759 if (!suppressTopBorder) {
1760 drawCellBorder(
this, painter, table, td, cell, clipped,
QCss::TopEdge,
1761 -1,
true,
true, rowStartsOnPageTop);
1764 drawCellBorder(
this, painter, table, td, cell, clipped,
QCss::LeftEdge,
1765 suppressTopBorder ? lastHeaderRow : -1,
1766 !doNotAdjustTopAnchor,
1767 !doNotAdjustBottomAnchor,
1768 rowStartsOnPageTop);
1769 drawCellBorder(
this, painter, table, td, cell, clipped,
QCss::RightEdge,
1770 suppressTopBorder ? lastHeaderRow : -1,
1771 !doNotAdjustTopAnchor,
1772 !doNotAdjustBottomAnchor,
1773 rowStartsOnPageTop);
1775 if (!suppressBottomBorder) {
1777 -1,
true,
true,
false);
1781 if (turn_off_antialiasing)
1782 painter->setRenderHint(QPainter::Antialiasing,
false);
1795 QTextBlock *cursorBlockNeedingRepaint, QPointF *cursorBlockOffset)
const
1797 QTextTableCell cell = table->cellAt(r, c);
1798 int rspan = cell.rowSpan();
1799 int cspan = cell.columnSpan();
1801 int cr = cell.row();
1806 int cc = cell.column();
1811 const QFixed leftPadding = td->leftPadding(table, cell);
1812 const QFixed topPadding = td->topPadding(table, cell);
1814 qreal topMargin = (td->effectiveTopMargin + td->cellSpacing + td->border).toReal();
1815 qreal bottomMargin = (td->effectiveBottomMargin + td->cellSpacing + td->border).toReal();
1817 const int headerRowCount = qMin(table->format().headerRowCount(), table->rows() - 1);
1818 if (r >= headerRowCount)
1819 topMargin += td->headerHeight.toReal();
1823 bool cellBorderConfigured = (cell.format().hasProperty(QTextFormat::TableCellLeftBorder) ||
1824 cell.format().hasProperty(QTextFormat::TableCellTopBorder) ||
1825 cell.format().hasProperty(QTextFormat::TableCellRightBorder) ||
1826 cell.format().hasProperty(QTextFormat::TableCellBottomBorder));
1829 const QBrush oldBrush = painter->brush();
1830 const QPen oldPen = painter->pen();
1834 const qreal border = 1;
1836 QRectF borderRect(cellRect.left() - border, cellRect.top() - border, cellRect.width() + border, cellRect.height() + border);
1839 QTextFrameFormat::BorderStyle cellBorder = table->format().borderStyle();
1840 switch (cellBorder) {
1841 case QTextFrameFormat::BorderStyle_Inset:
1842 cellBorder = QTextFrameFormat::BorderStyle_Outset;
1844 case QTextFrameFormat::BorderStyle_Outset:
1845 cellBorder = QTextFrameFormat::BorderStyle_Inset;
1847 case QTextFrameFormat::BorderStyle_Groove:
1848 cellBorder = QTextFrameFormat::BorderStyle_Ridge;
1850 case QTextFrameFormat::BorderStyle_Ridge:
1851 cellBorder = QTextFrameFormat::BorderStyle_Groove;
1857 drawBorder(painter, borderRect, topMargin, bottomMargin,
1858 border, table->format().borderBrush(), cellBorder);
1860 painter->setBrush(oldBrush);
1861 painter->setPen(oldPen);
1864 const QBrush bg = cell.format().background();
1865 const QPointF brushOrigin = painter->brushOriginF();
1866 if (bg.style() != Qt::NoBrush) {
1867 const qreal pageHeight = document->pageSize().height();
1868 const int topPage = pageHeight > 0 ?
static_cast<
int>(cellRect.top() / pageHeight) : 0;
1869 const int bottomPage = pageHeight > 0 ?
static_cast<
int>((cellRect.bottom()) / pageHeight) : 0;
1871 if (topPage == bottomPage)
1872 fillBackground(painter, cellRect, bg, cellRect.topLeft());
1874 for (
int i = topPage; i <= bottomPage; ++i) {
1875 QRectF clipped = cellRect.toRect();
1877 if (topPage != bottomPage) {
1878 const qreal top = qMax(i * pageHeight + topMargin, cell_context.clip.top());
1879 const qreal bottom = qMin((i + 1) * pageHeight - bottomMargin, cell_context.clip.bottom());
1881 clipped.setTop(qMax(clipped.top(), top));
1882 clipped.setBottom(qMin(clipped.bottom(), bottom));
1884 if (clipped.bottom() <= clipped.top())
1887 fillBackground(painter, clipped, bg, cellRect.topLeft());
1892 if (bg.style() > Qt::SolidPattern)
1893 painter->setBrushOrigin(cellRect.topLeft());
1900 const QFixed verticalOffset = td->cellVerticalOffsets.at(c + r * table->columns());
1902 const QPointF cellPos = QPointF(cellRect.left() + leftPadding.toReal(),
1903 cellRect.top() + (topPadding + verticalOffset).toReal());
1905 QTextBlock repaintBlock;
1906 drawFlow(cellPos, painter, cell_context, cell.begin(),
1907 td->childFrameMap.values(r + c * table->rows()),
1909 if (repaintBlock.isValid()) {
1910 *cursorBlockNeedingRepaint = repaintBlock;
1911 *cursorBlockOffset = cellPos;
1914 if (bg.style() > Qt::SolidPattern)
1915 painter->setBrushOrigin(brushOrigin);
1996 const QAbstractTextDocumentLayout::PaintContext &context,
1997 const QTextBlock &bl,
bool inRootFrame)
const
1999 const QTextLayout *tl = bl.layout();
2000 QRectF r = tl->boundingRect();
2001 r.translate(offset + tl->position());
2002 if (!bl.isVisible() || (context.clip.isValid() && (r.bottom() < context.clip.y() || r.top() > context.clip.bottom())))
2004 qCDebug(lcDraw) <<
"drawBlock" << bl.position() <<
"at" << offset <<
"br" << tl->boundingRect();
2006 QTextBlockFormat blockFormat = bl.blockFormat();
2008 QBrush bg = blockFormat.background();
2009 if (bg != Qt::NoBrush) {
2015 if (inRootFrame && document->pageSize().width() <= 0) {
2017 rect.setRight((fd->size.width - fd->rightMargin).toReal());
2021 if (!blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth))
2022 fillBackground(painter, rect, bg, r.topLeft());
2025 QList<QTextLayout::FormatRange> selections;
2026 int blpos = bl.position();
2027 int bllen = bl.length();
2028 const QTextCharFormat *selFormat =
nullptr;
2029 for (
int i = 0; i < context.selections.size(); ++i) {
2030 const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i);
2031 const int selStart = range.cursor.selectionStart() - blpos;
2032 const int selEnd = range.cursor.selectionEnd() - blpos;
2033 if (selStart < bllen && selEnd > 0
2034 && selEnd > selStart) {
2035 QTextLayout::FormatRange o;
2037 o.length = selEnd - selStart;
2038 o.format = range.format;
2039 selections.append(o);
2040 }
else if (! range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection)
2041 && bl.contains(range.cursor.position())) {
2044 QTextLayout::FormatRange o;
2045 QTextLine l = tl->lineForTextPosition(range.cursor.position() - blpos);
2046 o.start = l.textStart();
2047 o.length = l.textLength();
2048 if (o.start + o.length == bllen - 1)
2050 o.format = range.format;
2051 selections.append(o);
2053 if (selStart < 0 && selEnd >= 1)
2054 selFormat = &range.format;
2057 QTextObject *object = document->objectForFormat(bl.blockFormat());
2058 if (object && object->format().toListFormat().style() != QTextListFormat::ListStyleUndefined)
2059 drawListItem(offset, painter, context, bl, selFormat);
2061 QPen oldPen = painter->pen();
2062 painter->setPen(context.palette.color(QPalette::Text));
2064 tl->draw(painter, offset, selections, context.clip.isValid() ? (context.clip & clipRect) : clipRect);
2069 if (!isEmptyBlockBeforeTable(frameIteratorForTextPosition(blpos))
2070 && ((context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen)
2071 || (context.cursorPosition < -1 && !tl->preeditAreaText().isEmpty()))) {
2072 int cpos = context.cursorPosition;
2074 cpos = tl->preeditAreaPosition() - (cpos + 2);
2077 tl->drawCursor(painter, offset, cpos,
cursorWidth);
2080 if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
2081 const qreal width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth).value(r.width());
2082 const auto color = blockFormat.hasProperty(QTextFormat::BackgroundBrush)
2083 ? qvariant_cast<QBrush>(blockFormat.property(QTextFormat::BackgroundBrush)).color()
2084 : context.palette.color(QPalette::Inactive, QPalette::WindowText);
2085 painter->setPen(color);
2086 qreal y = r.bottom();
2087 if (bl.length() == 1)
2088 y = r.top() + r.height() / 2;
2090 const qreal middleX = r.left() + r.width() / 2;
2091 painter->drawLine(QLineF(middleX - width / 2, y, middleX + width / 2, y));
2094 painter->setPen(oldPen);
2099 const QAbstractTextDocumentLayout::PaintContext &context,
2100 const QTextBlock &bl,
const QTextCharFormat *selectionFormat)
const
2102 Q_Q(
const QTextDocumentLayout);
2103 const QTextBlockFormat blockFormat = bl.blockFormat();
2104 const QTextCharFormat charFormat = bl.charFormat();
2105 QFont font(charFormat.font());
2106 if (q->paintDevice())
2107 font = QFont(font, q->paintDevice());
2109 const QFontMetrics fontMetrics(font);
2110 QTextObject *
const object = document->objectForFormat(blockFormat);
2111 const QTextListFormat lf = object->format().toListFormat();
2112 int style = lf.style();
2116 if (blockFormat.hasProperty(QTextFormat::ListStyle))
2117 style = QTextListFormat::Style(blockFormat.intProperty(QTextFormat::ListStyle));
2119 QTextLayout *layout = bl.layout();
2120 if (layout->lineCount() == 0)
2122 QTextLine firstLine = layout->lineAt(0);
2123 Q_ASSERT(firstLine.isValid());
2124 QPointF pos = (offset + layout->position()).toPoint();
2125 Qt::LayoutDirection dir = bl.textDirection();
2127 QRectF textRect = firstLine.naturalTextRect();
2128 pos += textRect.topLeft().toPoint();
2129 if (dir == Qt::RightToLeft)
2130 pos.rx() += textRect.width();
2134 case QTextListFormat::ListDecimal:
2135 case QTextListFormat::ListLowerAlpha:
2136 case QTextListFormat::ListUpperAlpha:
2137 case QTextListFormat::ListLowerRoman:
2138 case QTextListFormat::ListUpperRoman:
2139 itemText =
static_cast<QTextList *>(object)->itemText(bl);
2140 size.setWidth(fontMetrics.horizontalAdvance(itemText));
2141 size.setHeight(fontMetrics.height());
2144 case QTextListFormat::ListSquare:
2145 case QTextListFormat::ListCircle:
2146 case QTextListFormat::ListDisc:
2147 size.setWidth(fontMetrics.lineSpacing() / 3);
2148 size.setHeight(size.width());
2151 case QTextListFormat::ListStyleUndefined:
2156 QRectF r(pos, size);
2158 qreal xoff = fontMetrics.horizontalAdvance(u' ');
2159 if (dir == Qt::LeftToRight)
2160 xoff = -xoff - size.width();
2161 r.translate( xoff, (fontMetrics.height() / 2) - (size.height() / 2));
2165 painter->setRenderHint(QPainter::Antialiasing);
2167 const bool marker = bl.blockFormat().marker() != QTextBlockFormat::MarkerType::NoMarker;
2168 if (selectionFormat) {
2169 painter->setPen(QPen(selectionFormat->foreground(), 0));
2171 painter->fillRect(r, selectionFormat->background());
2173 QBrush fg = charFormat.foreground();
2174 if (fg == Qt::NoBrush)
2175 fg = context.palette.text();
2176 painter->setPen(QPen(fg, 0));
2179 QBrush brush = context.palette.brush(QPalette::Text);
2182 int adj = fontMetrics.lineSpacing() / 6;
2183 r.adjust(-adj, 0, -adj, 0);
2184 const QRectF outer = r.adjusted(-adj, -adj, adj, adj);
2185 if (selectionFormat)
2186 painter->fillRect(outer, selectionFormat->background());
2187 if (bl.blockFormat().marker() == QTextBlockFormat::MarkerType::Checked) {
2190 painter->setPen(QPen(painter->pen().color(), 2));
2191 painter->drawLine(r.topLeft(), r.bottomRight());
2192 painter->drawLine(r.topRight(), r.bottomLeft());
2193 painter->setPen(QPen(painter->pen().color(), 0));
2195 painter->drawRect(outer);
2199 case QTextListFormat::ListDecimal:
2200 case QTextListFormat::ListLowerAlpha:
2201 case QTextListFormat::ListUpperAlpha:
2202 case QTextListFormat::ListLowerRoman:
2203 case QTextListFormat::ListUpperRoman: {
2204 QTextLayout layout(itemText, font, q->paintDevice());
2205 layout.setCacheEnabled(
true);
2206 QTextOption option(Qt::AlignLeft | Qt::AlignAbsolute);
2207 option.setTextDirection(dir);
2208 layout.setTextOption(option);
2209 layout.beginLayout();
2210 QTextLine line = layout.createLine();
2212 line.setLeadingIncluded(
true);
2214 layout.draw(painter, QPointF(r.left(), pos.y()));
2217 case QTextListFormat::ListSquare:
2219 painter->fillRect(r, painter->pen().brush());
2221 case QTextListFormat::ListCircle:
2223 painter->drawEllipse(r.translated(0.5, 0.5));
2225 case QTextListFormat::ListDisc:
2227 painter->setBrush(painter->pen().brush());
2228 painter->setPen(Qt::NoPen);
2229 painter->drawEllipse(r);
2232 case QTextListFormat::ListStyleUndefined:
2367 qCDebug(lcTable) <<
"layoutTable from" << layoutFrom <<
"to" << layoutTo <<
"parentY" << parentY;
2370 const int rows = table->rows();
2371 const int columns = table->columns();
2373 const QTextTableFormat fmt = table->format();
2375 td->childFrameMap.clear();
2377 const QList<QTextFrame *> children = table->childFrames();
2378 for (
int i = 0; i < children.size(); ++i) {
2379 QTextFrame *frame = children.at(i);
2380 QTextTableCell cell = table->cellAt(frame->firstPosition());
2381 td->childFrameMap.insert(cell.row() + cell.column() * rows, frame);
2385 QList<QTextLength> columnWidthConstraints = fmt.columnWidthConstraints();
2386 if (columnWidthConstraints.size() != columns)
2387 columnWidthConstraints.resize(columns);
2388 Q_ASSERT(columnWidthConstraints.size() == columns);
2396 const QFixed cellSpacing = td->cellSpacing = QFixed::fromReal(scaleToDevice(td->borderCollapse ? 0 : fmt.cellSpacing())).round();
2400 td->effectiveTopBorder = td->effectiveBottomBorder = td->effectiveLeftBorder = td->effectiveRightBorder = td->border;
2402#ifndef QT_NO_CSSPARSER
2407 outerBorders[i] = 0;
2409 for (
int r = 0; r < rows; ++r) {
2411 for (
int c = 0; c < columns; ++c)
2412 findWidestOutermostBorder(table, td, table->cellAt(r, c),
QCss::TopEdge, outerBorders);
2414 if (r == rows - 1) {
2415 for (
int c = 0; c < columns; ++c)
2416 findWidestOutermostBorder(table, td, table->cellAt(r, c),
QCss::BottomEdge, outerBorders);
2418 findWidestOutermostBorder(table, td, table->cellAt(r, 0),
QCss::LeftEdge, outerBorders);
2419 findWidestOutermostBorder(table, td, table->cellAt(r, columns - 1),
QCss::RightEdge, outerBorders);
2421 td->effectiveTopBorder = QFixed::fromReal(scaleToDevice(outerBorders[QCss::TopEdge] / 2)).round();
2422 td->effectiveBottomBorder = QFixed::fromReal(scaleToDevice(outerBorders[QCss::BottomEdge] / 2)).round();
2423 td->effectiveLeftBorder = QFixed::fromReal(scaleToDevice(outerBorders[QCss::LeftEdge] / 2)).round();
2424 td->effectiveRightBorder = QFixed::fromReal(scaleToDevice(outerBorders[QCss::RightEdge] / 2)).round();
2428 td->deviceScale = scaleToDevice(qreal(1));
2429 td->cellPadding = QFixed::fromReal(scaleToDevice(fmt.cellPadding()));
2430 const QFixed leftMargin = td->leftMargin + td->padding + td->effectiveLeftBorder;
2431 const QFixed rightMargin = td->rightMargin + td->padding + td->effectiveRightBorder;
2432 const QFixed topMargin = td->topMargin + td->padding + td->effectiveTopBorder;
2434 const QFixed absoluteTableY = parentY + td->position.y;
2436 const QTextOption::WrapMode oldDefaultWrapMode = docPrivate->defaultTextOption.wrapMode();
2438recalc_minmax_widths:
2440 QFixed remainingWidth = td->contentsWidth;
2442 remainingWidth -= columns * 2 * td->borderCell;
2444 remainingWidth -= (columns - 1) * cellSpacing;
2446 remainingWidth -= 2 * cellSpacing;
2449 remainingWidth -= td->effectiveLeftBorder;
2450 remainingWidth -= td->effectiveRightBorder;
2454 const QFixed initialTotalWidth = remainingWidth;
2456 td->widths.resize(columns);
2459 td->minWidths.resize(columns);
2463 td->minWidths.fill(1);
2465 td->maxWidths.resize(columns);
2466 td->maxWidths.fill(QFIXED_MAX);
2469 for (
int i = 0; i < columns; ++i) {
2470 for (
int row = 0; row < rows; ++row) {
2471 const QTextTableCell cell = table->cellAt(row, i);
2472 const int cspan = cell.columnSpan();
2474 if (cspan > 1 && i != cell.column())
2477 const QFixed leftPadding = td->leftPadding(table, cell);
2478 const QFixed rightPadding = td->rightPadding(table, cell);
2479 const QFixed widthPadding = leftPadding + rightPadding;
2484 QTextLayoutStruct layoutStruct = layoutCell(table, cell, QFIXED_MAX, layoutFrom,
2485 layoutTo, td, absoluteTableY,
2489 QFixed widthToDistribute = layoutStruct.minimumWidth + widthPadding;
2490 for (
int n = 0; n < cspan; ++n) {
2491 const int col = i + n;
2492 QFixed w = widthToDistribute / (cspan - n);
2494 td->minWidths[col] = qMax(td->minWidths.at(col), w).ceil();
2495 widthToDistribute -= td->minWidths.at(col);
2496 if (widthToDistribute <= 0)
2500 QFixed maxW = td->maxWidths.at(i);
2501 if (layoutStruct.maximumWidth != QFIXED_MAX) {
2502 if (maxW == QFIXED_MAX)
2503 maxW = layoutStruct.maximumWidth + widthPadding;
2505 maxW = qMax(maxW, layoutStruct.maximumWidth + widthPadding);
2507 if (maxW == QFIXED_MAX)
2516 widthToDistribute = maxW;
2517 for (
int n = 0; n < cspan; ++n) {
2518 const int col = i + n;
2519 QFixed w = widthToDistribute / (cspan - n);
2520 if (td->maxWidths[col] != QFIXED_MAX)
2521 w = qMax(td->maxWidths[col], w);
2522 td->maxWidths[col] = qMax(td->minWidths.at(col), w);
2523 widthToDistribute -= td->maxWidths.at(col);
2524 if (widthToDistribute <= 0)
2532 QFixed totalPercentage;
2533 int variableCols = 0;
2534 QFixed totalMinWidth = 0;
2535 for (
int i = 0; i < columns; ++i) {
2536 const QTextLength &length = columnWidthConstraints.at(i);
2537 if (length.type() == QTextLength::FixedLength) {
2538 td->minWidths[i] = td->widths[i] = qMax(scaleToDevice(QFixed::fromReal(length.rawValue())), td->minWidths.at(i));
2539 remainingWidth -= td->widths.at(i);
2540 qCDebug(lcTable) <<
"column" << i <<
"has width constraint" << td->minWidths.at(i) <<
"px, remaining width now" << remainingWidth;
2541 }
else if (length.type() == QTextLength::PercentageLength) {
2542 totalPercentage += QFixed::fromReal(length.rawValue());
2543 }
else if (length.type() == QTextLength::VariableLength) {
2546 td->widths[i] = td->minWidths.at(i);
2547 remainingWidth -= td->minWidths.at(i);
2548 qCDebug(lcTable) <<
"column" << i <<
"has variable width, min" << td->minWidths.at(i) <<
"remaining width now" << remainingWidth;
2550 totalMinWidth += td->minWidths.at(i);
2555 const QFixed totalPercentagedWidth = initialTotalWidth * totalPercentage / 100;
2556 QFixed remainingMinWidths = totalMinWidth;
2557 for (
int i = 0; i < columns; ++i) {
2558 remainingMinWidths -= td->minWidths.at(i);
2559 if (columnWidthConstraints.at(i).type() == QTextLength::PercentageLength) {
2560 const QFixed allottedPercentage = QFixed::fromReal(columnWidthConstraints.at(i).rawValue());
2562 const QFixed percentWidth = totalPercentagedWidth * allottedPercentage / totalPercentage;
2563 QFixed maxWidth = remainingWidth - remainingMinWidths;
2564 if (percentWidth >= td->minWidths.at(i) && maxWidth > td->minWidths.at(i)) {
2565 td->widths[i] = qBound(td->minWidths.at(i), percentWidth, maxWidth);
2567 td->widths[i] = td->minWidths.at(i);
2569 qCDebug(lcTable) <<
"column" << i <<
"has width constraint" << columnWidthConstraints.at(i).rawValue()
2570 <<
"%, allocated width" << td->widths[i] <<
"remaining width now" << remainingWidth;
2571 remainingWidth -= td->widths.at(i);
2577 if (variableCols > 0 && remainingWidth > 0) {
2578 QVarLengthArray<
int> columnsWithProperMaxSize;
2579 for (
int i = 0; i < columns; ++i)
2580 if (columnWidthConstraints.at(i).type() == QTextLength::VariableLength
2581 && td->maxWidths.at(i) != QFIXED_MAX)
2582 columnsWithProperMaxSize.append(i);
2584 QFixed lastRemainingWidth = remainingWidth;
2585 while (remainingWidth > 0) {
2586 for (
int k = 0; k < columnsWithProperMaxSize.size(); ++k) {
2587 const int col = columnsWithProperMaxSize[k];
2588 const int colsLeft = columnsWithProperMaxSize.size() - k;
2589 const QFixed w = qMin(td->maxWidths.at(col) - td->widths.at(col), remainingWidth / colsLeft);
2590 td->widths[col] += w;
2591 remainingWidth -= w;
2593 if (remainingWidth == lastRemainingWidth)
2595 lastRemainingWidth = remainingWidth;
2598 if (remainingWidth > 0
2600 && fmt.width().type() != QTextLength::VariableLength) {
2601 const QFixed widthPerAnySizedCol = remainingWidth / variableCols;
2602 for (
int col = 0; col < columns; ++col) {
2603 if (columnWidthConstraints.at(col).type() == QTextLength::VariableLength)
2604 td->widths[col] += widthPerAnySizedCol;
2615 for (
int i = 0; i < columns; ++i) {
2616 QFixed orig = td->widths[i];
2617 td->widths[i] = (td->widths[i] - error).round();
2618 error = td->widths[i] - orig;
2621 td->columnPositions.resize(columns);
2622 td->columnPositions[0] = leftMargin + cellSpacing + td->border;
2624 for (
int i = 1; i < columns; ++i)
2625 td->columnPositions[i] = td->columnPositions.at(i-1) + td->widths.at(i-1) + 2 * td->borderCell + cellSpacing;
2628 const QFixed contentsWidth = td->columnPositions.constLast() + td->widths.constLast() + td->padding + td->border + cellSpacing - leftMargin;
2632 if (docPrivate->defaultTextOption.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere
2633 && contentsWidth > td->contentsWidth) {
2634 docPrivate->defaultTextOption.setWrapMode(QTextOption::WrapAnywhere);
2636 goto recalc_minmax_widths;
2639 td->contentsWidth = contentsWidth;
2641 docPrivate->defaultTextOption.setWrapMode(oldDefaultWrapMode);
2643 td->heights.resize(rows);
2644 td->heights.fill(0);
2646 td->rowPositions.resize(rows);
2647 td->rowPositions[0] = topMargin + cellSpacing + td->border;
2649 bool haveRowSpannedCells =
false;
2652 QList<QFixed> cellHeights;
2653 cellHeights.reserve(rows * columns);
2655 QFixed pageHeight = QFixed::fromReal(document->pageSize().height());
2656 if (pageHeight <= 0)
2657 pageHeight = QFIXED_MAX;
2659 QList<QFixed> heightToDistribute;
2660 heightToDistribute.resize(columns);
2662 td->headerHeight = 0;
2663 const int headerRowCount = qMin(table->format().headerRowCount(), rows - 1);
2664 const QFixed originalTopMargin = td->effectiveTopMargin;
2665 bool hasDroppedTable =
false;
2671 for (
int r = 0; r < rows; ++r) {
2674 const int tableStartPage = (absoluteTableY / pageHeight).truncate();
2675 const int currentPage = ((td->rowPositions.at(r) + absoluteTableY) / pageHeight).truncate();
2676 const QFixed pageBottom = (currentPage + 1) * pageHeight - td->effectiveBottomMargin - absoluteTableY - cellSpacing - td->border;
2677 const QFixed pageTop = currentPage * pageHeight + td->effectiveTopMargin - absoluteTableY + cellSpacing + td->border;
2678 const QFixed nextPageTop = pageTop + pageHeight;
2680 if (td->rowPositions.at(r) > pageBottom)
2681 td->rowPositions[r] = nextPageTop;
2682 else if (td->rowPositions.at(r) < pageTop)
2683 td->rowPositions[r] = pageTop;
2685 bool dropRowToNextPage =
true;
2686 int cellCountBeforeRow = cellHeights.size();
2690 QFixed dropDistance = 0;
2693 const int rowStartPage = ((td->rowPositions.at(r) + absoluteTableY) / pageHeight).truncate();
2696 if (r <= headerRowCount && rowStartPage > tableStartPage && !hasDroppedTable) {
2697 td->rowPositions[0] = nextPageTop;
2698 cellHeights.clear();
2699 td->effectiveTopMargin = originalTopMargin;
2700 hasDroppedTable =
true;
2705 int rowCellCount = 0;
2706 for (
int c = 0; c < columns; ++c) {
2707 QTextTableCell cell = table->cellAt(r, c);
2708 const int rspan = cell.rowSpan();
2709 const int cspan = cell.columnSpan();
2711 if (cspan > 1 && cell.column() != c)
2715 haveRowSpannedCells =
true;
2717 const int cellRow = cell.row();
2720 if (cellRow + rspan - 1 == r)
2721 td->heights[r] = qMax(td->heights.at(r), heightToDistribute.at(c) - dropDistance).round();
2726 const QFixed topPadding = td->topPadding(table, cell);
2727 const QFixed bottomPadding = td->bottomPadding(table, cell);
2728 const QFixed leftPadding = td->leftPadding(table, cell);
2729 const QFixed rightPadding = td->rightPadding(table, cell);
2730 const QFixed widthPadding = leftPadding + rightPadding;
2734 const QFixed width = td->cellWidth(c, cspan) - widthPadding;
2736 layoutFrom, layoutTo,
2740 const QFixed height = (layoutStruct.y + bottomPadding + topPadding).round();
2743 heightToDistribute[c] = height + dropDistance;
2745 td->heights[r] = qMax(td->heights.at(r), height);
2747 cellHeights.append(layoutStruct.y);
2749 QFixed childPos = td->rowPositions.at(r) + topPadding + flowPosition(cell.begin());
2750 if (childPos < pageBottom)
2751 dropRowToNextPage =
false;
2754 if (rowCellCount > 0 && dropRowToNextPage) {
2755 dropDistance = nextPageTop - td->rowPositions.at(r);
2756 td->rowPositions[r] = nextPageTop;
2758 dropRowToNextPage =
false;
2759 cellHeights.resize(cellCountBeforeRow);
2760 if (r > headerRowCount)
2761 td->heights[r - 1] = pageBottom - td->rowPositions.at(r - 1);
2765 if (haveRowSpannedCells) {
2766 const QFixed effectiveHeight = td->heights.at(r) + td->borderCell + cellSpacing + td->borderCell;
2767 for (
int c = 0; c < columns; ++c)
2768 heightToDistribute[c] = qMax(heightToDistribute.at(c) - effectiveHeight - dropDistance, QFixed(0));
2771 if (r == headerRowCount - 1) {
2772 td->headerHeight = td->rowPositions.at(r) + td->heights.at(r) - td->rowPositions.at(0) + td->cellSpacing + 2 * td->borderCell;
2773 td->headerHeight -= td->headerHeight * (td->headerHeight / pageHeight).truncate();
2774 td->effectiveTopMargin += td->headerHeight;
2778 td->effectiveTopMargin = originalTopMargin;
2782 td->cellVerticalOffsets.resize(rows * columns);
2784 for (
int r = 0; r < rows; ++r) {
2785 for (
int c = 0; c < columns; ++c) {
2786 QTextTableCell cell = table->cellAt(r, c);
2787 if (cell.row() != r || cell.column() != c)
2790 const int rowSpan = cell.rowSpan();
2791 const QFixed availableHeight = td->rowPositions.at(r + rowSpan - 1) + td->heights.at(r + rowSpan - 1) - td->rowPositions.at(r);
2793 const QTextCharFormat cellFormat = cell.format();
2794 const QFixed cellHeight = cellHeights.at(cellIndex++) + td->topPadding(table, cell) + td->bottomPadding(table, cell);
2797 switch (cellFormat.verticalAlignment()) {
2798 case QTextCharFormat::AlignMiddle:
2799 offset = (availableHeight - cellHeight) / 2;
2801 case QTextCharFormat::AlignBottom:
2802 offset = availableHeight - cellHeight;
2808 for (
int rd = 0; rd < cell.rowSpan(); ++rd) {
2809 for (
int cd = 0; cd < cell.columnSpan(); ++cd) {
2810 const int index = (c + cd) + (r + rd) * columns;
2811 td->cellVerticalOffsets[index] = offset;
2817 td->minimumWidth = td->columnPositions.at(0);
2818 for (
int i = 0; i < columns; ++i) {
2819 td->minimumWidth += td->minWidths.at(i) + 2 * td->borderCell + cellSpacing;
2821 td->minimumWidth += rightMargin - td->border;
2823 td->maximumWidth = td->columnPositions.at(0);
2824 for (
int i = 0; i < columns; ++i) {
2825 if (td->maxWidths.at(i) != QFIXED_MAX)
2826 td->maximumWidth += td->maxWidths.at(i) + 2 * td->borderCell + cellSpacing;
2827 qCDebug(lcTable) <<
"column" << i <<
"has final width" << td->widths.at(i).toReal()
2828 <<
"min" << td->minWidths.at(i).toReal() <<
"max" << td->maxWidths.at(i).toReal();
2830 td->maximumWidth += rightMargin - td->border;
2924 qCDebug(lcLayout,
"layoutFrame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame());
2928 QFixed newContentsWidth;
2930 bool fullLayout =
false;
2932 QTextFrameFormat fformat = f->frameFormat();
2934 QFixed tm = QFixed::fromReal(scaleToDevice(fformat.topMargin())).round();
2935 if (tm != fd->topMargin) {
2939 QFixed bm = QFixed::fromReal(scaleToDevice(fformat.bottomMargin())).round();
2940 if (bm != fd->bottomMargin) {
2941 fd->bottomMargin = bm;
2944 fd->leftMargin = QFixed::fromReal(scaleToDevice(fformat.leftMargin())).round();
2945 fd->rightMargin = QFixed::fromReal(scaleToDevice(fformat.rightMargin())).round();
2946 QFixed b = QFixed::fromReal(scaleToDevice(fformat.border())).round();
2947 if (b != fd->border) {
2951 QFixed p = QFixed::fromReal(scaleToDevice(fformat.padding())).round();
2952 if (p != fd->padding) {
2957 QTextFrame *parent = f->parentFrame();
2962 fd->effectiveTopMargin = pd->effectiveTopMargin + fd->topMargin + fd->border + fd->padding;
2963 fd->effectiveBottomMargin = pd->effectiveBottomMargin + fd->topMargin + fd->border + fd->padding;
2965 if (qobject_cast<QTextTable *>(parent)) {
2967 fd->effectiveTopMargin += td->cellSpacing + td->border + td->cellPadding;
2968 fd->effectiveBottomMargin += td->cellSpacing + td->border + td->cellPadding;
2971 fd->effectiveTopMargin = fd->topMargin + fd->border + fd->padding;
2972 fd->effectiveBottomMargin = fd->bottomMargin + fd->border + fd->padding;
2975 newContentsWidth = frameWidth - 2*(fd->border + fd->padding)
2976 - fd->leftMargin - fd->rightMargin;
2978 if (frameHeight != -1) {
2979 fd->contentsHeight = frameHeight - 2*(fd->border + fd->padding)
2980 - fd->topMargin - fd->bottomMargin;
2982 fd->contentsHeight = frameHeight;
2991 if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
2992 fd->contentsWidth = newContentsWidth;
2993 return layoutTable(table, layoutFrom, layoutTo, parentY);
2999 fd->contentsWidth = newContentsWidth;
3003 layoutStruct.x_left = fd->leftMargin + fd->border + fd->padding;
3004 layoutStruct.x_right = layoutStruct.x_left + newContentsWidth;
3005 layoutStruct.y = fd->topMargin + fd->border + fd->padding;
3006 layoutStruct.frameY = parentY + fd->position.y;
3007 layoutStruct.contentsWidth = 0;
3008 layoutStruct.minimumWidth = 0;
3009 layoutStruct.maximumWidth = QFIXED_MAX;
3010 layoutStruct
.fullLayout = fullLayout || (fd->oldContentsWidth != newContentsWidth);
3011 layoutStruct.updateRect = QRectF(QPointF(0, 0), QSizeF(qreal(INT_MAX), qreal(INT_MAX)));
3012 qCDebug(lcLayout) <<
"layoutStruct: x_left" << layoutStruct.x_left <<
"x_right" << layoutStruct.x_right
3013 <<
"fullLayout" << layoutStruct.fullLayout;
3014 fd->oldContentsWidth = newContentsWidth;
3016 layoutStruct.pageHeight = QFixed::fromReal(document->pageSize().height());
3017 if (layoutStruct.pageHeight < 0)
3018 layoutStruct.pageHeight = QFIXED_MAX;
3020 const int currentPage = layoutStruct.pageHeight == 0 ? 0 : (layoutStruct.frameY / layoutStruct.pageHeight).truncate();
3021 layoutStruct.pageTopMargin = fd->effectiveTopMargin;
3022 layoutStruct.pageBottomMargin = fd->effectiveBottomMargin;
3023 layoutStruct.pageBottom = (currentPage + 1) * layoutStruct.pageHeight - layoutStruct.pageBottomMargin;
3025 if (!f->parentFrame())
3028 QTextFrame::Iterator it = f->begin();
3029 layoutFlow(it, &layoutStruct, layoutFrom, layoutTo);
3031 QFixed maxChildFrameWidth = 0;
3032 QList<QTextFrame *> children = f->childFrames();
3033 for (
int i = 0; i < children.size(); ++i) {
3034 QTextFrame *c = children.at(i);
3036 maxChildFrameWidth = qMax(maxChildFrameWidth, cd->size.width);
3039 const QFixed marginWidth = 2*(fd->border + fd->padding) + fd->leftMargin + fd->rightMargin;
3040 if (!f->parentFrame()) {
3041 idealWidth = qMax(maxChildFrameWidth, layoutStruct.contentsWidth).toReal();
3042 idealWidth += marginWidth.toReal();
3045 QFixed actualWidth = qMax(newContentsWidth, qMax(maxChildFrameWidth, layoutStruct.contentsWidth));
3046 fd->contentsWidth = actualWidth;
3047 if (newContentsWidth <= 0) {
3048 fd->contentsWidth = newContentsWidth;
3051 fd->minimumWidth = layoutStruct.minimumWidth;
3052 fd->maximumWidth = layoutStruct.maximumWidth;
3054 fd->size.height = fd->contentsHeight == -1
3055 ? layoutStruct.y + fd->border + fd->padding + fd->bottomMargin
3056 : fd->contentsHeight + 2*(fd->border + fd->padding) + fd->topMargin + fd->bottomMargin;
3057 fd->size.width = actualWidth + marginWidth;
3059 if (layoutStruct.updateRectForFloats.isValid())
3060 layoutStruct.updateRect |= layoutStruct.updateRectForFloats;
3061 return layoutStruct.updateRect;
3065 int layoutFrom,
int layoutTo, QFixed width)
3067 qCDebug(lcLayout) <<
"layoutFlow from=" << layoutFrom <<
"to=" << layoutTo;
3072 QTextFrame::Iterator previousIt;
3074 const bool inRootFrame = (it.parentFrame() == document->rootFrame());
3076 bool redoCheckPoints = layoutStruct->fullLayout || checkPoints.isEmpty();
3078 if (!redoCheckPoints) {
3079 auto checkPoint = std::lower_bound(checkPoints.begin(), checkPoints.end(), layoutFrom);
3080 if (checkPoint != checkPoints.end()) {
3081 if (checkPoint != checkPoints.begin())
3084 layoutStruct->y = checkPoint->y;
3085 layoutStruct->frameY = checkPoint->frameY;
3086 layoutStruct->minimumWidth = checkPoint->minimumWidth;
3087 layoutStruct->maximumWidth = checkPoint->maximumWidth;
3088 layoutStruct->contentsWidth = checkPoint->contentsWidth;
3090 if (layoutStruct->pageHeight > 0) {
3092 layoutStruct->pageBottom = (page + 1) * layoutStruct->pageHeight - layoutStruct->pageBottomMargin;
3095 it = frameIteratorForTextPosition(checkPoint->positionInFrame);
3096 checkPoints.resize(checkPoint - checkPoints.begin() + 1);
3098 if (checkPoint != checkPoints.begin()) {
3103 redoCheckPoints =
true;
3107 if (redoCheckPoints) {
3108 checkPoints.clear();
3110 cp.y = layoutStruct->y;
3111 cp.frameY = layoutStruct->frameY;
3113 cp.minimumWidth = layoutStruct->minimumWidth;
3114 cp.maximumWidth = layoutStruct->maximumWidth;
3115 cp.contentsWidth = layoutStruct->contentsWidth;
3116 checkPoints.append(cp);
3120 QTextBlockFormat previousBlockFormat = previousIt.currentBlock().blockFormat();
3122 QFixed maximumBlockWidth = 0;
3123 while (!it.atEnd() && layoutStruct->absoluteY() < QFIXED_MAX) {
3124 QTextFrame *c = it.currentFrame();
3127 if (it.currentFrame())
3128 docPos = it.currentFrame()->firstPosition();
3130 docPos = it.currentBlock().position();
3133 if (qAbs(layoutStruct->y - checkPoints.constLast().y) > 2000) {
3135 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
3136 if (left == layoutStruct->x_left && right == layoutStruct->x_right) {
3138 p.y = layoutStruct->y;
3139 p.frameY = layoutStruct->frameY;
3141 p.minimumWidth = layoutStruct->minimumWidth;
3142 p.maximumWidth = layoutStruct->maximumWidth;
3143 p.contentsWidth = layoutStruct->contentsWidth;
3144 checkPoints.append(p);
3158 QTextFrameFormat fformat = c->frameFormat();
3160 if (fformat.position() == QTextFrameFormat::InFlow) {
3161 if (fformat.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore)
3165 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
3166 left = qMax(left, layoutStruct->x_left);
3167 right = qMin(right, layoutStruct->x_right);
3169 if (right - left < cd->size.width) {
3170 layoutStruct->y = findY(layoutStruct->y, layoutStruct, cd->size.width);
3171 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
3174 QFixedPoint pos(left, layoutStruct->y);
3176 Qt::Alignment align = Qt::AlignLeft;
3178 QTextTable *table = qobject_cast<QTextTable *>(c);
3181 align = table->format().alignment() & Qt::AlignHorizontal_Mask;
3185 if (inRootFrame && !(align & Qt::AlignLeft))
3190 if (document->pageSize().height() > 0.0f)
3195 layoutFrame(c, layoutFrom, layoutTo, width, -1, layoutStruct->frameY);
3197 layoutFrame(c, layoutFrom, layoutTo, layoutStruct->frameY);
3199 QFixed absoluteChildPos = table ? pos.y +
static_cast<QTextTableData *>(data(table))->rowPositions.at(0) : pos.y + firstChildPos(c);
3200 absoluteChildPos += layoutStruct->frameY;
3203 if (absoluteChildPos > layoutStruct->pageBottom) {
3205 pos.y = layoutStruct->y;
3211 layoutFrame(c, layoutFrom, layoutTo, width, -1, layoutStruct->frameY);
3213 layoutFrame(c, layoutFrom, layoutTo, layoutStruct->frameY);
3218 if (right - left > cd->size.width) {
3219 if (align & Qt::AlignRight)
3220 pos.x += layoutStruct->x_right - cd->size.width;
3221 else if (align & Qt::AlignHCenter)
3222 pos.x += (layoutStruct->x_right - cd->size.width) / 2;
3227 layoutStruct->y += cd->size.height;
3229 layoutStruct->pageBottom = (page + 1) * layoutStruct->pageHeight - layoutStruct->pageBottomMargin;
3233 if (c->frameFormat().pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter)
3236 QRectF oldFrameRect(cd->position.toPointF(), cd->size.toSizeF());
3240 updateRect = layoutFrame(c, layoutFrom, layoutTo);
3246 updateRect = layoutFrame(c, layoutFrom, layoutTo);
3248 QRectF frameRect(cd->position.toPointF(), cd->size.toSizeF());
3250 if (frameRect == oldFrameRect && updateRect.isValid())
3251 updateRect.translate(cd->position.toPointF());
3253 updateRect = frameRect;
3256 if (oldFrameRect.isValid())
3260 layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, cd->minimumWidth);
3261 layoutStruct->maximumWidth = qMin(layoutStruct->maximumWidth, cd->maximumWidth);
3266 QTextFrame::Iterator lastIt;
3267 if (!previousIt.atEnd() && previousIt != it)
3268 lastIt = previousIt;
3270 QTextBlock block = it.currentBlock();
3273 const QTextBlockFormat blockFormat = block.blockFormat();
3275 if (blockFormat.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore)
3278 const QFixed origY = layoutStruct->y;
3279 const QFixed origPageBottom = layoutStruct->pageBottom;
3280 const QFixed origMaximumWidth = layoutStruct->maximumWidth;
3281 layoutStruct->maximumWidth = 0;
3283 const QTextBlockFormat *previousBlockFormatPtr =
nullptr;
3284 if (lastIt.currentBlock().isValid())
3285 previousBlockFormatPtr = &previousBlockFormat;
3288 layoutBlock(block
, docPos
, blockFormat
, layoutStruct
, layoutFrom
, layoutTo
, previousBlockFormatPtr
);
3292 if (inRootFrame && !(block.layout()->textOption().alignment() & Qt::AlignLeft))
3297 if (isEmptyBlockBeforeTable(block, blockFormat, it)) {
3298 const QTextBlock lastBlock = lastIt.currentBlock();
3299 const qreal lastBlockBottomMargin = lastBlock.isValid() ? lastBlock.blockFormat().bottomMargin() : 0.0f;
3300 layoutStruct->y = origY + QFixed::fromReal(qMax(lastBlockBottomMargin, block.blockFormat().topMargin()));
3301 layoutStruct->pageBottom = origPageBottom;
3304 if (isEmptyBlockAfterTable(block, lastIt.currentFrame())) {
3306 QTextLayout *layout = block.layout();
3308 QPointF pos((td->position.x + td->size.width).toReal(),
3309 (td->position.y + td->size.height).toReal() - layout->boundingRect().height());
3311 layout->setPosition(pos);
3312 layoutStruct->y = origY;
3313 layoutStruct->pageBottom = origPageBottom;
3317 if (isLineSeparatorBlockAfterTable(block, lastIt.currentFrame())) {
3319 QTextLayout *layout = block.layout();
3321 QFixed height = layout->lineCount() > 0 ? QFixed::fromReal(layout->lineAt(0).height()) : QFixed();
3323 if (layoutStruct->pageBottom == origPageBottom) {
3324 layoutStruct->y -= height;
3325 layout->setPosition(layout->position() - QPointF(0, height.toReal()));
3328 layoutStruct->y = origY - height;
3329 layoutStruct->pageBottom = origPageBottom;
3330 layoutBlock(block
, docPos
, blockFormat
, layoutStruct
, layoutFrom
, layoutTo
, previousBlockFormatPtr
);
3333 if (layout->lineCount() > 0) {
3334 QPointF linePos((td->position.x + td->size.width).toReal(),
3335 (td->position.y + td->size.height - height).toReal());
3337 layout->lineAt(0).setPosition(linePos - layout->position());
3341 if (blockFormat.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter)
3345 maximumBlockWidth = qMax(maximumBlockWidth, layoutStruct->maximumWidth);
3346 layoutStruct->maximumWidth = origMaximumWidth;
3347 previousBlockFormat = blockFormat;
3350 if (layoutStruct->maximumWidth == QFIXED_MAX && maximumBlockWidth > 0)
3351 layoutStruct->maximumWidth = maximumBlockWidth;
3353 layoutStruct->maximumWidth = qMax(layoutStruct->maximumWidth, maximumBlockWidth);
3358 if (!qobject_cast<QTextTable *>(layoutStruct
->frame)) {
3359 QList<QTextFrame *> children = layoutStruct
->frame->childFrames();
3360 for (
int i = 0; i < children.size(); ++i) {
3362 if (!fd->layoutDirty && children.at(i)->frameFormat().position() != QTextFrameFormat::InFlow)
3363 layoutStruct->y = qMax(layoutStruct->y, fd->position.y + fd->size.height);
3370 if (!fd->floats.isEmpty())
3373 if (it.atEnd() || layoutStruct->absoluteY() >= QFIXED_MAX) {
3377 cp.y = layoutStruct->y;
3378 cp.positionInFrame = docPrivate->length();
3379 cp.minimumWidth = layoutStruct->minimumWidth;
3380 cp.maximumWidth = layoutStruct->maximumWidth;
3381 cp.contentsWidth = layoutStruct->contentsWidth;
3382 checkPoints.append(cp);
3383 checkPoints.reserve(checkPoints.size());
3385 currentLazyLayoutPosition = checkPoints.constLast().positionInFrame;
3418 QTextLayoutStruct *layoutStruct,
int layoutFrom,
int layoutTo,
const QTextBlockFormat *previousBlockFormat)
3420 Q_Q(QTextDocumentLayout);
3421 if (!bl.isVisible())
3424 QTextLayout *tl = bl.layout();
3425 const int blockLength = bl.length();
3427 qCDebug(lcLayout) <<
"layoutBlock from=" << layoutFrom <<
"to=" << layoutTo
3428 <<
"; width" << layoutStruct->x_right - layoutStruct->x_left <<
"(maxWidth is btw" << tl->maximumWidth() <<
')';
3430 if (previousBlockFormat) {
3431 qreal margin = qMax(blockFormat.topMargin(), previousBlockFormat->bottomMargin());
3432 if (margin > 0 && q->paintDevice()) {
3433 margin *= qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi());
3435 layoutStruct->y += QFixed::fromReal(margin);
3440 Qt::LayoutDirection dir = bl.textDirection();
3443 if (docPrivate->defaultTextOption.flags() & QTextOption::AddSpaceForLineAndParagraphSeparators) {
3444 QFontMetricsF fm(bl.charFormat().font());
3445 extraMargin = QFixed::fromReal(fm.horizontalAdvance(u'\x21B5'));
3448 const QFixed indent =
this->blockIndent(blockFormat);
3449 const QFixed totalLeftMargin = QFixed::fromReal(blockFormat.leftMargin()) + (dir == Qt::RightToLeft ? extraMargin : indent);
3450 const QFixed totalRightMargin = QFixed::fromReal(blockFormat.rightMargin()) + (dir == Qt::RightToLeft ? indent : extraMargin);
3452 const QPointF oldPosition = tl->position();
3453 tl->setPosition(QPointF(layoutStruct->x_left.toReal(), layoutStruct->y.toReal()));
3455 if (layoutStruct->fullLayout
3456 || (blockPosition + blockLength > layoutFrom && blockPosition <= layoutTo)
3458 || (layoutStruct->pageHeight != QFIXED_MAX && layoutStruct->absoluteY() + QFixed::fromReal(tl->boundingRect().height()) > layoutStruct->pageBottom)) {
3460 qCDebug(lcLayout) <<
"do layout";
3461 QTextOption option = docPrivate->defaultTextOption;
3462 option.setTextDirection(dir);
3463 option.setTabs( blockFormat.tabPositions() );
3465 Qt::Alignment align = docPrivate->defaultTextOption.alignment();
3466 if (blockFormat.hasProperty(QTextFormat::BlockAlignment))
3467 align = blockFormat.alignment();
3468 option.setAlignment(QGuiApplicationPrivate::visualAlignment(dir, align));
3470 if (blockFormat.nonBreakableLines() || document->pageSize().width() < 0) {
3471 option.setWrapMode(QTextOption::ManualWrap);
3474 tl->setTextOption(option);
3476 const bool haveWordOrAnyWrapMode = (option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere);
3479 const QFixed cy = layoutStruct->y;
3480 const QFixed l = layoutStruct->x_left + totalLeftMargin;
3481 const QFixed r = layoutStruct->x_right - totalRightMargin;
3485 bool firstLine =
true;
3487 QTextLine line = tl->createLine();
3488 if (!line.isValid())
3490 line.setLeadingIncluded(
true);
3493 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
3494 left = qMax(left, l);
3495 right = qMin(right, r);
3498 text_indent = QFixed::fromReal(blockFormat.textIndent());
3499 if (dir == Qt::LeftToRight)
3500 left += text_indent;
3502 right -= text_indent;
3507 if (fixedColumnWidth != -1)
3508 line.setNumColumns(fixedColumnWidth, (right - left).toReal());
3510 line.setLineWidth((right - left).toReal());
3513 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
3514 left = qMax(left, l);
3515 right = qMin(right, r);
3516 if (dir == Qt::LeftToRight)
3517 left += text_indent;
3519 right -= text_indent;
3521 if (fixedColumnWidth == -1 && QFixed::fromReal(line.naturalTextWidth()) > right-left) {
3523 layoutStruct->pendingFloats.clear();
3525 line.setLineWidth((right-left).toReal());
3526 if (QFixed::fromReal(line.naturalTextWidth()) > right-left) {
3527 if (haveWordOrAnyWrapMode) {
3528 option.setWrapMode(QTextOption::WrapAnywhere);
3529 tl->setTextOption(option);
3532 layoutStruct->pendingFloats.clear();
3534 layoutStruct->y = findY(layoutStruct->y, layoutStruct, QFixed::fromReal(line.naturalTextWidth()));
3535 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
3536 left = qMax(left, l);
3537 right = qMin(right, r);
3538 if (dir == Qt::LeftToRight)
3539 left += text_indent;
3541 right -= text_indent;
3542 line.setLineWidth(qMax<qreal>(line.naturalTextWidth(), (right-left).toReal()));
3544 if (haveWordOrAnyWrapMode) {
3545 option.setWrapMode(QTextOption::WordWrap);
3546 tl->setTextOption(option);
3552 QFixed lineBreakHeight, lineHeight, lineAdjustment, lineBottom;
3553 qreal scaling = (q->paintDevice() && q->paintDevice()->logicalDpiY() != qt_defaultDpi()) ?
3554 qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi()) : 1;
3555 getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight, &lineBottom);
3557 while (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom &&
3558 layoutStruct->contentHeight() >= lineBreakHeight) {
3559 if (layoutStruct->pageHeight == QFIXED_MAX) {
3560 layoutStruct->y = QFIXED_MAX - layoutStruct->frameY;
3566 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
3567 left = qMax(left, l);
3568 right = qMin(right, r);
3569 if (dir == Qt::LeftToRight)
3570 left += text_indent;
3572 right -= text_indent;
3575 line.setPosition(QPointF((left - layoutStruct->x_left).toReal(), (layoutStruct->y - cy - lineAdjustment).toReal()));
3576 bottom = layoutStruct->y + lineBottom;
3577 layoutStruct->y += lineHeight;
3578 layoutStruct->contentsWidth
3579 = qMax<QFixed>(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + line.naturalTextWidth()) + totalRightMargin);
3582 for (
int i = 0; i < layoutStruct->pendingFloats.size(); ++i) {
3583 QTextFrame *f = layoutStruct->pendingFloats.at(i);
3586 layoutStruct->pendingFloats.clear();
3588 layoutStruct->y = qMax(layoutStruct->y, bottom);
3591 const int cnt = tl->lineCount();
3593 for (
int i = 0; i < cnt; ++i) {
3594 qCDebug(lcLayout) <<
"going to move text line" << i;
3595 QTextLine line = tl->lineAt(i);
3596 layoutStruct->contentsWidth
3597 = qMax(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + tl->lineAt(i).naturalTextWidth()) + totalRightMargin);
3599 QFixed lineBreakHeight, lineHeight, lineAdjustment, lineBottom;
3600 qreal scaling = (q->paintDevice() && q->paintDevice()->logicalDpiY() != qt_defaultDpi()) ?
3601 qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi()) : 1;
3602 getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight, &lineBottom);
3604 if (layoutStruct->pageHeight != QFIXED_MAX) {
3605 if (layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom)
3607 line.setPosition(QPointF(line.position().x(), (layoutStruct->y - lineAdjustment).toReal() - tl->position().y()));
3609 bottom = layoutStruct->y + lineBottom;
3610 layoutStruct->y += lineHeight;
3612 layoutStruct->y = qMax(layoutStruct->y, bottom);
3613 if (layoutStruct->updateRect.isValid()
3614 && blockLength > 1) {
3615 if (layoutFrom >= blockPosition + blockLength) {
3619 layoutStruct->updateRect.setTop(qMax(layoutStruct->updateRect.top(), layoutStruct->y.toReal()));
3620 }
else if (layoutTo < blockPosition) {
3621 if (oldPosition == tl->position())
3626 layoutStruct->updateRect.setBottom(qMin(layoutStruct->updateRect.bottom(), tl->position().y()));
3628 layoutStruct->updateRect.setBottom(qreal(INT_MAX));
3634 const QFixed margins = totalLeftMargin + totalRightMargin;
3635 layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, QFixed::fromReal(tl->minimumWidth()) + margins);
3637 const QFixed maxW = QFixed::fromReal(tl->maximumWidth()) + margins;
3640 if (layoutStruct->maximumWidth == QFIXED_MAX)
3641 layoutStruct->maximumWidth = maxW;
3643 layoutStruct->maximumWidth = qMax(layoutStruct->maximumWidth, maxW);