[api] Move text wrapping handling logic from layout table code to QgsTextRenderer

Allows other users of QgsTextRenderer to take advantage of the
automatic line wrapping behaviour
This commit is contained in:
Nyall Dawson 2021-11-28 16:43:13 +10:00
parent dcf0cfe5a3
commit fadfb477d9
12 changed files with 204 additions and 127 deletions

View File

@ -1169,6 +1169,13 @@ QgsStringUtils.AllSmallCaps.__doc__ = "Force all characters to small caps (since
Qgis.Capitalization.__doc__ = 'String capitalization options.\n\n.. note::\n\n Prior to QGIS 3.24 this was available as :py:class:`QgsStringUtils`.Capitalization\n\n.. versionadded:: 3.24\n\n' + '* ``MixedCase``: ' + Qgis.Capitalization.MixedCase.__doc__ + '\n' + '* ``AllUppercase``: ' + Qgis.Capitalization.AllUppercase.__doc__ + '\n' + '* ``AllLowercase``: ' + Qgis.Capitalization.AllLowercase.__doc__ + '\n' + '* ``ForceFirstLetterToCapital``: ' + Qgis.Capitalization.ForceFirstLetterToCapital.__doc__ + '\n' + '* ``SmallCaps``: ' + Qgis.Capitalization.SmallCaps.__doc__ + '\n' + '* ``TitleCase``: ' + Qgis.Capitalization.TitleCase.__doc__ + '\n' + '* ``UpperCamelCase``: ' + Qgis.Capitalization.UpperCamelCase.__doc__ + '\n' + '* ``AllSmallCaps``: ' + Qgis.Capitalization.AllSmallCaps.__doc__
# --
Qgis.Capitalization.baseClass = Qgis
# monkey patching scoped based enum
Qgis.TextRendererFlag.WrapLines.__doc__ = "Automatically wrap long lines of text"
Qgis.TextRendererFlag.__doc__ = 'Flags which control the behavior of rendering text.\n\n.. versionadded:: 3.24\n\n' + '* ``WrapLines``: ' + Qgis.TextRendererFlag.WrapLines.__doc__
# --
Qgis.TextRendererFlag.baseClass = Qgis
Qgis.TextRendererFlags.baseClass = Qgis
TextRendererFlags = Qgis # dirty hack since SIP seems to introduce the flags in module
QgsCurve.Orientation = Qgis.AngularDirection
# monkey patching scoped based enum
QgsCurve.Clockwise = Qgis.AngularDirection.Clockwise

View File

@ -750,6 +750,12 @@ The development version
AllSmallCaps,
};
enum class TextRendererFlag
{
WrapLines,
};
typedef QFlags<Qgis::TextRendererFlag> TextRendererFlags;
enum class AngularDirection
{
@ -858,6 +864,8 @@ QFlags<Qgis::VectorLayerTypeFlag> operator|(Qgis::VectorLayerTypeFlag f1, QFlags
QFlags<Qgis::MarkerLinePlacement> operator|(Qgis::MarkerLinePlacement f1, QFlags<Qgis::MarkerLinePlacement> f2);
QFlags<Qgis::TextRendererFlag> operator|(Qgis::TextRendererFlag f1, QFlags<Qgis::TextRendererFlag> f2);

View File

@ -87,7 +87,8 @@ Calculates pixel size (considering output size should be in pixel or map units,
static void drawText( const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines,
QgsRenderContext &context, const QgsTextFormat &format,
bool drawAsOutlines = true, VAlignment vAlignment = AlignTop );
bool drawAsOutlines = true, VAlignment vAlignment = AlignTop,
Qgis::TextRendererFlags flags = Qgis::TextRendererFlags() );
%Docstring
Draws text within a rectangle using the specified settings.
@ -102,6 +103,7 @@ Draws text within a rectangle using the specified settings.
rendering and may result in side effects like misaligned text buffers. This setting is deprecated and has no effect
as of QGIS 3.4.3 and the text format should be set using :py:func:`QgsRenderContext.setTextRenderFormat()` instead.
:param vAlignment: vertical alignment (since QGIS 3.16)
:param flags: text rendering flags (since QGIS 3.24)
%End
static void drawText( QPointF point, double rotation, HAlignment alignment, const QStringList &textLines,
@ -196,7 +198,7 @@ Returns the width of a text based on a given format.
%End
static double textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode = Point,
QFontMetricsF *fontMetrics = 0 );
QFontMetricsF *fontMetrics = 0, Qgis::TextRendererFlags flags = Qgis::TextRendererFlags(), double maxLineWidth = 0 );
%Docstring
Returns the height of a text based on a given format.
@ -205,6 +207,8 @@ Returns the height of a text based on a given format.
:param textLines: list of lines of text to calculate width from
:param mode: draw mode
:param fontMetrics: font metrics
:param flags: text renderer flags (since QGIS 3.24)
:param maxLineWidth: maximum line width, in painter units. Used when the Qgis.TextRendererFlag.WrapLines flag is used (since QGIS 3.24)
%End
static double textHeight( const QgsRenderContext &context, const QgsTextFormat &format, QChar character, bool includeEffects = false );
@ -218,6 +222,24 @@ Returns the height of a character when rendered with the specified text ``format
returned height. If ``False``, then the returned size considers the character only.
.. versionadded:: 3.16
%End
static bool textRequiresWrapping( const QgsRenderContext &context, const QString &text, double width, const QgsTextFormat &format );
%Docstring
Returns ``True`` if the specified ``text`` requires line wrapping in order to fit within the specified ``width`` (in painter units).
.. seealso:: :py:func:`wrappedText`
.. versionadded:: 3.24
%End
static QStringList wrappedText( const QgsRenderContext &context, const QString &text, double width, const QgsTextFormat &format );
%Docstring
Wraps a ``text`` string to multiple lines, such that each individual line will fit within the specified ``width`` (in painter units).
.. seealso:: :py:func:`textRequiresWrapping`
.. versionadded:: 3.24
%End
static const double FONT_WORKAROUND_SCALE;

View File

@ -470,13 +470,7 @@ void QgsLayoutTable::render( QgsLayoutItemRenderContext &context, const QRectF &
const QRectF textCell = QRectF( currentX, currentY + mCellMargin, mMaxColumnWidthMap[col], cellHeaderHeight - 2 * mCellMargin );
// disable text clipping to target text rectangle, because we manually clip to the full cell bounds below
// and it's ok if text overlaps into the margin (e.g. extenders or italicized text)
QStringList str = column.heading().split( '\n' );
if ( ( mWrapBehavior != TruncateText || column.width() > 0 ) && textRequiresWrapping( context.renderContext(), column.heading(), column.width(), headerFormat ) )
{
str = wrappedText( context.renderContext(), column.heading(), column.width(), headerFormat );
}
const QStringList str = column.heading().split( '\n' );
// scale to dots
{
@ -485,7 +479,9 @@ void QgsLayoutTable::render( QgsLayoutItemRenderContext &context, const QRectF &
textCell.top() * context.renderContext().scaleFactor(),
textCell.width() * context.renderContext().scaleFactor(),
textCell.height() * context.renderContext().scaleFactor() ), 0,
headerAlign, str, context.renderContext(), headerFormat, true, QgsTextRenderer::AlignVCenter );
headerAlign, str, context.renderContext(), headerFormat, true, QgsTextRenderer::AlignVCenter,
mWrapBehavior == WrapText ? Qgis::TextRendererFlag::WrapLines : Qgis::TextRendererFlags()
);
}
currentX += mMaxColumnWidthMap[ col ];
@ -514,6 +510,7 @@ void QgsLayoutTable::render( QgsLayoutItemRenderContext &context, const QRectF &
for ( const QgsLayoutTableColumn &column : std::as_const( mColumns ) )
{
( void )column;
const QRectF fullCell( currentX, currentY, mMaxColumnWidthMap[col] + 2 * mCellMargin, rowHeight );
//draw background
p->save();
@ -527,19 +524,12 @@ void QgsLayoutTable::render( QgsLayoutItemRenderContext &context, const QRectF &
QVariant cellContents = mTableContents.at( row ).at( col );
const QString localizedString { QgsExpressionUtils::toLocalizedString( cellContents ) };
QStringList str = localizedString.split( '\n' );
const QStringList str = localizedString.split( '\n' );
QgsTextFormat cellFormat = textFormatForCell( row, col );
QgsExpressionContextScopePopper popper( context.renderContext().expressionContext(), scopeForCell( row, col ) );
cellFormat.updateDataDefinedProperties( context.renderContext() );
// disable text clipping to target text rectangle, because we manually clip to the full cell bounds below
// and it's ok if text overlaps into the margin (e.g. extenders or italicized text)
if ( ( mWrapBehavior != TruncateText || column.width() > 0 ) && textRequiresWrapping( context.renderContext(), localizedString, column.width(), cellFormat ) )
{
str = wrappedText( context.renderContext(), localizedString, column.width(), cellFormat );
}
p->save();
p->setClipRect( fullCell );
const QRectF textCell = QRectF( currentX, currentY + mCellMargin, mMaxColumnWidthMap[col], rowHeight - 2 * mCellMargin );
@ -559,7 +549,8 @@ void QgsLayoutTable::render( QgsLayoutItemRenderContext &context, const QRectF &
textCell.width() * context.renderContext().scaleFactor(),
textCell.height() * context.renderContext().scaleFactor() ), 0,
QgsTextRenderer::convertQtHAlignment( horizontalAlignmentForCell( row, col ) ), str, context.renderContext(), cellFormat, true,
QgsTextRenderer::convertQtVAlignment( verticalAlignmentForCell( row, col ) ) );
QgsTextRenderer::convertQtVAlignment( verticalAlignmentForCell( row, col ) ),
mWrapBehavior == WrapText ? Qgis::TextRendererFlag::WrapLines : Qgis::TextRendererFlags() );
}
p->restore();
@ -1189,16 +1180,16 @@ bool QgsLayoutTable::calculateMaxRowHeights()
{
heights[i] = 0;
}
else if ( textRequiresWrapping( context, col.heading(), mColumns.at( i ).width(), cellFormat ) )
{
//contents too wide for cell, need to wrap
heights[i] = QgsTextRenderer::textHeight( context, cellFormat, wrappedText( context, col.heading(), mColumns.at( i ).width(), cellFormat ), QgsTextRenderer::Rect )
/ context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters )
- headerDescentMm;
}
else
{
heights[i] = QgsTextRenderer::textHeight( context, cellFormat, QStringList() << col.heading(), QgsTextRenderer::Rect ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters )
heights[i] = QgsTextRenderer::textHeight( context,
cellFormat,
QStringList() << col.heading(), QgsTextRenderer::Rect,
nullptr,
mWrapBehavior == WrapText ? Qgis::TextRendererFlag::WrapLines : Qgis::TextRendererFlags(),
context.convertToPainterUnits( mColumns.at( i ).width(), QgsUnitTypes::RenderMillimeters )
)
/ context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters )
- headerDescentMm;
}
i++;
@ -1219,15 +1210,14 @@ bool QgsLayoutTable::calculateMaxRowHeights()
const double contentDescentMm = QgsTextRenderer::fontMetrics( context, cellFormat, QgsTextRenderer::FONT_WORKAROUND_SCALE ).descent() / QgsTextRenderer::FONT_WORKAROUND_SCALE / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
const QString localizedString { QgsExpressionUtils::toLocalizedString( *colIt ) };
if ( textRequiresWrapping( context, localizedString, mColumns.at( i ).width(), cellFormat ) )
{
//contents too wide for cell, need to wrap
heights[ row * cols + i ] = QgsTextRenderer::textHeight( context, cellFormat, wrappedText( context, localizedString, mColumns.at( i ).width(), cellFormat ), QgsTextRenderer::Rect ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters ) - contentDescentMm;
}
else
{
heights[ row * cols + i ] = QgsTextRenderer::textHeight( context, cellFormat, QStringList() << localizedString.split( '\n' ), QgsTextRenderer::Rect ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters ) - contentDescentMm;
}
heights[ row * cols + i ] = QgsTextRenderer::textHeight( context,
cellFormat,
QStringList() << localizedString.split( '\n' ),
QgsTextRenderer::Rect,
nullptr,
mWrapBehavior == WrapText ? Qgis::TextRendererFlag::WrapLines : Qgis::TextRendererFlags(),
context.convertToPainterUnits( mColumns.at( i ).width(), QgsUnitTypes::RenderMillimeters )
) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters ) - contentDescentMm;
i++;
}
@ -1375,83 +1365,6 @@ void QgsLayoutTable::drawHorizontalGridLines( QgsLayoutItemRenderContext &contex
painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
}
bool QgsLayoutTable::textRequiresWrapping( QgsRenderContext &context, const QString &text, double columnWidth, const QgsTextFormat &format ) const
{
if ( qgsDoubleNear( columnWidth, 0.0 ) || mWrapBehavior != WrapText )
return false;
const QStringList multiLineSplit = text.split( '\n' );
const double currentTextWidth = QgsTextRenderer::textWidth( context, format, multiLineSplit ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
return currentTextWidth > columnWidth;
}
QStringList QgsLayoutTable::wrappedText( QgsRenderContext &context, const QString &value, double columnWidth, const QgsTextFormat &format ) const
{
QStringList lines = value.split( '\n' );
QStringList outLines;
const auto constLines = lines;
for ( const QString &line : constLines )
{
if ( textRequiresWrapping( context, line, columnWidth, format ) )
{
//first step is to identify words which must be on their own line (too long to fit)
QStringList words = line.split( ' ' );
QStringList linesToProcess;
QString wordsInCurrentLine;
const auto constWords = words;
for ( const QString &word : constWords )
{
if ( textRequiresWrapping( context, word, columnWidth, format ) )
{
//too long to fit
if ( !wordsInCurrentLine.isEmpty() )
linesToProcess << wordsInCurrentLine;
wordsInCurrentLine.clear();
linesToProcess << word;
}
else
{
if ( !wordsInCurrentLine.isEmpty() )
wordsInCurrentLine.append( ' ' );
wordsInCurrentLine.append( word );
}
}
if ( !wordsInCurrentLine.isEmpty() )
linesToProcess << wordsInCurrentLine;
const auto constLinesToProcess = linesToProcess;
for ( const QString &line : constLinesToProcess )
{
QString remainingText = line;
int lastPos = remainingText.lastIndexOf( ' ' );
while ( lastPos > -1 )
{
//check if remaining text is short enough to go in one line
if ( !textRequiresWrapping( context, remainingText, columnWidth, format ) )
{
break;
}
if ( !textRequiresWrapping( context, remainingText.left( lastPos ), columnWidth, format ) )
{
outLines << remainingText.left( lastPos );
remainingText = remainingText.mid( lastPos + 1 );
lastPos = 0;
}
lastPos = remainingText.lastIndexOf( ' ', lastPos - 1 );
}
outLines << remainingText;
}
}
else
{
outLines << line;
}
}
return outLines;
}
QColor QgsLayoutTable::backgroundColor( int row, int column ) const
{
QColor color = mBackgroundColor;

View File

@ -771,10 +771,6 @@ class CORE_EXPORT QgsLayoutTable: public QgsLayoutMultiFrame
//! Initializes cell style map
void initStyles();
bool textRequiresWrapping( QgsRenderContext &context, const QString &text, double columnWidth, const QgsTextFormat &format ) const;
QStringList wrappedText( QgsRenderContext &context, const QString &value, double columnWidth, const QgsTextFormat &format ) const;
/**
* Returns the calculated background color for a row and column combination.
* \param row row number, where -1 is the header row, and 0 is the first body row

View File

@ -1223,6 +1223,18 @@ class CORE_EXPORT Qgis
};
Q_ENUM( Capitalization )
/**
* Flags which control the behavior of rendering text.
*
* \since QGIS 3.24
*/
enum class TextRendererFlag : int
{
WrapLines = 1 << 0, //!< Automatically wrap long lines of text
};
Q_ENUM( TextRendererFlag )
Q_DECLARE_FLAGS( TextRendererFlags, TextRendererFlag )
Q_FLAG( TextRendererFlags )
/**
* Angular directions.
@ -1367,6 +1379,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::MapSettingsFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::RenderContextFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::VectorLayerTypeFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::MarkerLinePlacements )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::TextRendererFlags )
// hack to workaround warnings when casting void pointers

View File

@ -77,13 +77,26 @@ int QgsTextRenderer::sizeToPixel( double size, const QgsRenderContext &c, QgsUni
return static_cast< int >( c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 ); //NOLINT
}
void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool, VAlignment vAlignment )
void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &text, QgsRenderContext &context, const QgsTextFormat &format, bool, VAlignment vAlignment, Qgis::TextRendererFlags flags )
{
QgsTextFormat tmpFormat = format;
if ( format.dataDefinedProperties().hasActiveProperties() ) // note, we use format instead of tmpFormat here, it's const and potentially avoids a detach
tmpFormat.updateDataDefinedProperties( context );
tmpFormat = updateShadowPosition( tmpFormat );
QStringList textLines;
for ( const QString &line : text )
{
if ( flags & Qgis::TextRendererFlag::WrapLines && textRequiresWrapping( context, line, rect.width(), format ) )
{
textLines.append( wrappedText( context, line, rect.width(), format ) );
}
else
{
textLines.append( line );
}
}
QgsTextDocument document = format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( textLines ) : QgsTextDocument::fromPlainText( textLines );
document.applyCapitalization( format.capitalization() );
@ -599,15 +612,28 @@ double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTex
return width / scaleFactor;
}
double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode, QFontMetricsF * )
double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode, QFontMetricsF *, Qgis::TextRendererFlags flags, double maxLineWidth )
{
QStringList lines;
for ( const QString &line : textLines )
{
if ( flags & Qgis::TextRendererFlag::WrapLines && maxLineWidth > 0 && textRequiresWrapping( context, line, maxLineWidth, format ) )
{
lines.append( wrappedText( context, line, maxLineWidth, format ) );
}
else
{
lines.append( line );
}
}
if ( !format.allowHtmlFormatting() )
{
return textHeight( context, format, QgsTextDocument::fromPlainText( textLines ), mode );
return textHeight( context, format, QgsTextDocument::fromPlainText( lines ), mode );
}
else
{
return textHeight( context, format, QgsTextDocument::fromHtml( textLines ), mode );
return textHeight( context, format, QgsTextDocument::fromHtml( lines ), mode );
}
}
@ -648,6 +674,80 @@ double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTe
return height + maxExtension;
}
bool QgsTextRenderer::textRequiresWrapping( const QgsRenderContext &context, const QString &text, double width, const QgsTextFormat &format )
{
if ( qgsDoubleNear( width, 0.0 ) )
return false;
const QStringList multiLineSplit = text.split( '\n' );
const double currentTextWidth = QgsTextRenderer::textWidth( context, format, multiLineSplit );
return currentTextWidth > width;
}
QStringList QgsTextRenderer::wrappedText( const QgsRenderContext &context, const QString &text, double width, const QgsTextFormat &format )
{
const QStringList lines = text.split( '\n' );
QStringList outLines;
for ( const QString &line : lines )
{
if ( textRequiresWrapping( context, line, width, format ) )
{
//first step is to identify words which must be on their own line (too long to fit)
const QStringList words = line.split( ' ' );
QStringList linesToProcess;
QString wordsInCurrentLine;
for ( const QString &word : words )
{
if ( textRequiresWrapping( context, word, width, format ) )
{
//too long to fit
if ( !wordsInCurrentLine.isEmpty() )
linesToProcess << wordsInCurrentLine;
wordsInCurrentLine.clear();
linesToProcess << word;
}
else
{
if ( !wordsInCurrentLine.isEmpty() )
wordsInCurrentLine.append( ' ' );
wordsInCurrentLine.append( word );
}
}
if ( !wordsInCurrentLine.isEmpty() )
linesToProcess << wordsInCurrentLine;
for ( const QString &line : std::as_const( linesToProcess ) )
{
QString remainingText = line;
int lastPos = remainingText.lastIndexOf( ' ' );
while ( lastPos > -1 )
{
//check if remaining text is short enough to go in one line
if ( !textRequiresWrapping( context, remainingText, width, format ) )
{
break;
}
if ( !textRequiresWrapping( context, remainingText.left( lastPos ), width, format ) )
{
outLines << remainingText.left( lastPos );
remainingText = remainingText.mid( lastPos + 1 );
lastPos = 0;
}
lastPos = remainingText.lastIndexOf( ' ', lastPos - 1 );
}
outLines << remainingText;
}
}
else
{
outLines << line;
}
}
return outLines;
}
double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &doc, DrawMode mode )
{
QgsTextDocument document = doc;

View File

@ -115,10 +115,12 @@ class CORE_EXPORT QgsTextRenderer
* rendering and may result in side effects like misaligned text buffers. This setting is deprecated and has no effect
* as of QGIS 3.4.3 and the text format should be set using QgsRenderContext::setTextRenderFormat() instead.
* \param vAlignment vertical alignment (since QGIS 3.16)
* \param flags text rendering flags (since QGIS 3.24)
*/
static void drawText( const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines,
QgsRenderContext &context, const QgsTextFormat &format,
bool drawAsOutlines = true, VAlignment vAlignment = AlignTop );
bool drawAsOutlines = true, VAlignment vAlignment = AlignTop,
Qgis::TextRendererFlags flags = Qgis::TextRendererFlags() );
/**
* Draws text at a point origin using the specified settings.
@ -212,9 +214,11 @@ class CORE_EXPORT QgsTextRenderer
* \param textLines list of lines of text to calculate width from
* \param mode draw mode
* \param fontMetrics font metrics
* \param flags text renderer flags (since QGIS 3.24)
* \param maxLineWidth maximum line width, in painter units. Used when the Qgis::TextRendererFlag::WrapLines flag is used (since QGIS 3.24)
*/
static double textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode = Point,
QFontMetricsF *fontMetrics = nullptr );
QFontMetricsF *fontMetrics = nullptr, Qgis::TextRendererFlags flags = Qgis::TextRendererFlags(), double maxLineWidth = 0 );
/**
* Returns the height of a character when rendered with the specified text \a format.
@ -229,6 +233,22 @@ class CORE_EXPORT QgsTextRenderer
*/
static double textHeight( const QgsRenderContext &context, const QgsTextFormat &format, QChar character, bool includeEffects = false );
/**
* Returns TRUE if the specified \a text requires line wrapping in order to fit within the specified \a width (in painter units).
*
* \see wrappedText()
* \since QGIS 3.24
*/
static bool textRequiresWrapping( const QgsRenderContext &context, const QString &text, double width, const QgsTextFormat &format );
/**
* Wraps a \a text string to multiple lines, such that each individual line will fit within the specified \a width (in painter units).
*
* \see textRequiresWrapping()
* \since QGIS 3.24
*/
static QStringList wrappedText( const QgsRenderContext &context, const QString &text, double width, const QgsTextFormat &format );
/**
* Scale factor for upscaling font sizes and downscaling destination painter devices.
*

View File

@ -37,6 +37,7 @@
#include "qgslayoutatlas.h"
#include "qgslayoututils.h"
#include "qgspallabeling.h"
#include "qgstextrenderer.h"
#include <QObject>
#include "qgstest.h"
@ -1644,18 +1645,15 @@ void TestQgsLayoutTable::wrappedText()
{
QgsProject p;
QgsLayout l( &p );
QgsLayoutItemAttributeTable *t = new QgsLayoutItemAttributeTable( &l );
t->setWrapBehavior( QgsLayoutTable::WrapText );
const QFont f;
const QString sourceText( "Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua" );
QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( &l, nullptr );
const QString wrapText = t->wrappedText( context, sourceText, 101 /*columnWidth*/, QgsTextFormat::fromQFont( f ) ).join( '\n' );
const QString wrapText = QgsTextRenderer::wrappedText( context, sourceText, context.convertToPainterUnits( 101, QgsUnitTypes::RenderMillimeters ) /*columnWidth*/, QgsTextFormat::fromQFont( f ) ).join( '\n' );
//there should be no line break before the last word (bug #20546)
QVERIFY( !wrapText.endsWith( "\naliqua" ) );
}
void TestQgsLayoutTable::testBaseSort()
{
QgsLayout l( QgsProject::instance() );

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB