mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Correctly render vertical oriented text using HTML formatting
This commit is contained in:
parent
670c762f75
commit
254c43df5f
@ -39,7 +39,7 @@ based on the resultant font metrics. Failure to do so will result in poor qualit
|
||||
at small font sizes.
|
||||
%End
|
||||
|
||||
QSizeF documentSize( Qgis::TextLayoutMode mode ) const;
|
||||
QSizeF documentSize( Qgis::TextLayoutMode mode, Qgis::TextOrientation orientation ) const;
|
||||
%Docstring
|
||||
Returns the overall size of the document.
|
||||
%End
|
||||
@ -57,6 +57,21 @@ Returns the height of the block at the specified index.
|
||||
double baselineOffset( int blockIndex, Qgis::TextLayoutMode mode ) const;
|
||||
%Docstring
|
||||
Returns the offset from the top of the document to the text baseline for the given block index.
|
||||
%End
|
||||
|
||||
double verticalOrientationXOffset( int blockIndex ) const;
|
||||
%Docstring
|
||||
Returns the vertical orientation x offset for the specified block.
|
||||
%End
|
||||
|
||||
double blockMaximumCharacterWidth( int blockIndex ) const;
|
||||
%Docstring
|
||||
Returns the maximum character width for the specified block.
|
||||
%End
|
||||
|
||||
double blockMaximumDescent( int blockIndex ) const;
|
||||
%Docstring
|
||||
Returns the maximum descent encountered in the specified block.
|
||||
%End
|
||||
|
||||
QFont fragmentFont( int blockIndex, int fragmentIndex ) const;
|
||||
|
@ -1628,7 +1628,7 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, const QSt
|
||||
document->splitLines( wrapchr, evalAutoWrapLength, useMaxLineLengthForAutoWrap );
|
||||
|
||||
*documentMetrics = QgsTextDocumentMetrics::calculateMetrics( *document, mFormat, *rc );
|
||||
const QSizeF size = documentMetrics->documentSize( Qgis::TextLayoutMode::Labeling );
|
||||
const QSizeF size = documentMetrics->documentSize( Qgis::TextLayoutMode::Labeling, orientation );
|
||||
w = size.width();
|
||||
h = size.height();
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ QgsTextDocumentMetrics QgsTextDocumentMetrics::calculateMetrics( const QgsTextDo
|
||||
double width = 0;
|
||||
double heightLabelMode = 0;
|
||||
double heightPointRectMode = 0;
|
||||
int i = 0;
|
||||
const int blockSize = document.size();
|
||||
res.mFragmentFonts.reserve( blockSize );
|
||||
double currentLabelBaseline = 0;
|
||||
@ -48,6 +47,10 @@ QgsTextDocumentMetrics QgsTextDocumentMetrics::calculateMetrics( const QgsTextDo
|
||||
double currentRectBaseline = 0;
|
||||
double lastLineLeading = 0;
|
||||
|
||||
double heightVerticalOrientation = 0;
|
||||
|
||||
QVector < double > blockVerticalLineSpacing;
|
||||
|
||||
for ( int blockIndex = 0; blockIndex < blockSize; blockIndex++ )
|
||||
{
|
||||
const QgsTextBlock &block = document.at( blockIndex );
|
||||
@ -55,12 +58,14 @@ QgsTextDocumentMetrics QgsTextDocumentMetrics::calculateMetrics( const QgsTextDo
|
||||
double blockWidth = 0;
|
||||
double blockHeightUsingAscentDescent = 0;
|
||||
double blockHeightUsingLineSpacing = 0;
|
||||
double blockHeightVerticalOrientation = 0;
|
||||
const int fragmentSize = block.size();
|
||||
|
||||
double maxBlockAscent = 0;
|
||||
double maxBlockDescent = 0;
|
||||
double maxLineSpacing = 0;
|
||||
double maxBlockLeading = 0;
|
||||
double maxBlockMaxWidth = 0;
|
||||
|
||||
QList< QFont > fragmentFonts;
|
||||
fragmentFonts.reserve( fragmentSize );
|
||||
@ -81,6 +86,7 @@ QgsTextDocumentMetrics QgsTextDocumentMetrics::calculateMetrics( const QgsTextDo
|
||||
blockHeightUsingLineSpacing = std::max( blockHeightUsingLineSpacing, fragmentHeightUsingLineSpacing );
|
||||
maxBlockAscent = std::max( maxBlockAscent, fm.ascent() / scaleFactor );
|
||||
maxBlockDescent = std::max( maxBlockDescent, fm.descent() / scaleFactor );
|
||||
maxBlockMaxWidth = std::max( maxBlockMaxWidth, fm.maxWidth() / scaleFactor );
|
||||
|
||||
if ( ( fm.lineSpacing() / scaleFactor ) > maxLineSpacing )
|
||||
{
|
||||
@ -89,6 +95,10 @@ QgsTextDocumentMetrics QgsTextDocumentMetrics::calculateMetrics( const QgsTextDo
|
||||
}
|
||||
|
||||
fragmentFonts << updatedFont;
|
||||
|
||||
const double verticalOrientationFragmentHeight = fragmentIndex == 0 ? ( fm.ascent() / scaleFactor * fragment.text().size() + ( fragment.text().size() - 1 ) * updatedFont.letterSpacing() / scaleFactor )
|
||||
: ( fragment.text().size() * ( fm.ascent() / scaleFactor + updatedFont.letterSpacing() / scaleFactor ) );
|
||||
blockHeightVerticalOrientation += verticalOrientationFragmentHeight;
|
||||
}
|
||||
|
||||
if ( blockIndex == 0 )
|
||||
@ -128,15 +138,19 @@ QgsTextDocumentMetrics QgsTextDocumentMetrics::calculateMetrics( const QgsTextDo
|
||||
res.mLastLineAscentOffset = 0.25 * maxBlockAscent;
|
||||
}
|
||||
|
||||
blockVerticalLineSpacing << ( format.lineHeightUnit() == QgsUnitTypes::RenderPercentage ? ( maxBlockMaxWidth * format.lineHeight() ) : lineHeightPainterUnits );
|
||||
|
||||
res.mBlockHeights << blockHeightUsingLineSpacing;
|
||||
|
||||
width = std::max( width, blockWidth );
|
||||
heightVerticalOrientation = std::max( heightVerticalOrientation, blockHeightVerticalOrientation );
|
||||
res.mBlockWidths << blockWidth;
|
||||
res.mFragmentFonts << fragmentFonts;
|
||||
res.mBaselineOffsetsLabelMode << currentLabelBaseline;
|
||||
res.mBaselineOffsetsPointMode << currentPointBaseline;
|
||||
res.mBaselineOffsetsRectMode << currentRectBaseline;
|
||||
i++;
|
||||
res.mBlockMaxDescent << maxBlockDescent;
|
||||
res.mBlockMaxCharacterWidth << maxBlockMaxWidth;
|
||||
|
||||
if ( blockIndex > 0 )
|
||||
lastLineLeading = maxBlockLeading;
|
||||
@ -160,20 +174,57 @@ QgsTextDocumentMetrics QgsTextDocumentMetrics::calculateMetrics( const QgsTextDo
|
||||
}
|
||||
}
|
||||
|
||||
if ( !res.mBlockMaxCharacterWidth.isEmpty() )
|
||||
{
|
||||
QList< double > adjustedRightToLeftXOffsets;
|
||||
double currentOffset = 0;
|
||||
const int size = res.mBlockMaxCharacterWidth.size();
|
||||
|
||||
double widthVerticalOrientation = 0;
|
||||
for ( int i = 0; i < size; ++i )
|
||||
{
|
||||
const double rightToLeftBlockMaxCharacterWidth = res.mBlockMaxCharacterWidth[size - 1 - i ];
|
||||
const double rightToLeftLineSpacing = blockVerticalLineSpacing[ size - 1 - i ];
|
||||
|
||||
adjustedRightToLeftXOffsets << currentOffset;
|
||||
currentOffset += rightToLeftLineSpacing;
|
||||
|
||||
if ( i == size - 1 )
|
||||
widthVerticalOrientation += rightToLeftBlockMaxCharacterWidth;
|
||||
else
|
||||
widthVerticalOrientation += rightToLeftLineSpacing;
|
||||
}
|
||||
std::reverse( adjustedRightToLeftXOffsets.begin(), adjustedRightToLeftXOffsets.end() );
|
||||
res.mVerticalOrientationXOffsets = adjustedRightToLeftXOffsets;
|
||||
|
||||
res.mDocumentSizeVerticalOrientation = QSizeF( widthVerticalOrientation, heightVerticalOrientation );
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
QSizeF QgsTextDocumentMetrics::documentSize( Qgis::TextLayoutMode mode ) const
|
||||
QSizeF QgsTextDocumentMetrics::documentSize( Qgis::TextLayoutMode mode, Qgis::TextOrientation orientation ) const
|
||||
{
|
||||
switch ( mode )
|
||||
switch ( orientation )
|
||||
{
|
||||
case Qgis::TextLayoutMode::Rectangle:
|
||||
case Qgis::TextLayoutMode::Point:
|
||||
return mDocumentSizePointRectMode;
|
||||
case Qgis::TextOrientation::Horizontal:
|
||||
switch ( mode )
|
||||
{
|
||||
case Qgis::TextLayoutMode::Rectangle:
|
||||
case Qgis::TextLayoutMode::Point:
|
||||
return mDocumentSizePointRectMode;
|
||||
|
||||
case Qgis::TextLayoutMode::Labeling:
|
||||
return mDocumentSizeLabelMode;
|
||||
case Qgis::TextLayoutMode::Labeling:
|
||||
return mDocumentSizeLabelMode;
|
||||
};
|
||||
BUILTIN_UNREACHABLE
|
||||
|
||||
case Qgis::TextOrientation::Vertical:
|
||||
return mDocumentSizeVerticalOrientation;
|
||||
case Qgis::TextOrientation::RotationBased:
|
||||
return QSizeF(); // label mode only
|
||||
}
|
||||
|
||||
BUILTIN_UNREACHABLE
|
||||
}
|
||||
|
||||
@ -201,6 +252,21 @@ double QgsTextDocumentMetrics::baselineOffset( int blockIndex, Qgis::TextLayoutM
|
||||
BUILTIN_UNREACHABLE
|
||||
}
|
||||
|
||||
double QgsTextDocumentMetrics::verticalOrientationXOffset( int blockIndex ) const
|
||||
{
|
||||
return mVerticalOrientationXOffsets.value( blockIndex );
|
||||
}
|
||||
|
||||
double QgsTextDocumentMetrics::blockMaximumCharacterWidth( int blockIndex ) const
|
||||
{
|
||||
return mBlockMaxCharacterWidth.value( blockIndex );
|
||||
}
|
||||
|
||||
double QgsTextDocumentMetrics::blockMaximumDescent( int blockIndex ) const
|
||||
{
|
||||
return mBlockMaxDescent.value( blockIndex );
|
||||
}
|
||||
|
||||
QFont QgsTextDocumentMetrics::fragmentFont( int blockIndex, int fragmentIndex ) const
|
||||
{
|
||||
return mFragmentFonts.value( blockIndex ).value( fragmentIndex );
|
||||
|
@ -55,7 +55,7 @@ class CORE_EXPORT QgsTextDocumentMetrics
|
||||
/**
|
||||
* Returns the overall size of the document.
|
||||
*/
|
||||
QSizeF documentSize( Qgis::TextLayoutMode mode ) const;
|
||||
QSizeF documentSize( Qgis::TextLayoutMode mode, Qgis::TextOrientation orientation ) const;
|
||||
|
||||
/**
|
||||
* Returns the width of the block at the specified index.
|
||||
@ -72,6 +72,21 @@ class CORE_EXPORT QgsTextDocumentMetrics
|
||||
*/
|
||||
double baselineOffset( int blockIndex, Qgis::TextLayoutMode mode ) const;
|
||||
|
||||
/**
|
||||
* Returns the vertical orientation x offset for the specified block.
|
||||
*/
|
||||
double verticalOrientationXOffset( int blockIndex ) const;
|
||||
|
||||
/**
|
||||
* Returns the maximum character width for the specified block.
|
||||
*/
|
||||
double blockMaximumCharacterWidth( int blockIndex ) const;
|
||||
|
||||
/**
|
||||
* Returns the maximum descent encountered in the specified block.
|
||||
*/
|
||||
double blockMaximumDescent( int blockIndex ) const;
|
||||
|
||||
/**
|
||||
* Returns the calculated font for the fragment at the specified block and fragment indices.
|
||||
*/
|
||||
@ -86,6 +101,7 @@ class CORE_EXPORT QgsTextDocumentMetrics
|
||||
|
||||
QSizeF mDocumentSizeLabelMode;
|
||||
QSizeF mDocumentSizePointRectMode;
|
||||
QSizeF mDocumentSizeVerticalOrientation;
|
||||
|
||||
QList < QList< QFont > > mFragmentFonts;
|
||||
QList< double > mBlockWidths;
|
||||
@ -93,6 +109,9 @@ class CORE_EXPORT QgsTextDocumentMetrics
|
||||
QList< double > mBaselineOffsetsLabelMode;
|
||||
QList< double > mBaselineOffsetsPointMode;
|
||||
QList< double > mBaselineOffsetsRectMode;
|
||||
QList< double > mVerticalOrientationXOffsets;
|
||||
QList< double > mBlockMaxDescent;
|
||||
QList< double > mBlockMaxCharacterWidth;
|
||||
double mFirstLineAscentOffset = 0;
|
||||
double mLastLineAscentOffset = 0;
|
||||
|
||||
|
@ -217,10 +217,10 @@ void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, Qgis::TextH
|
||||
case Qgis::TextVerticalAlignment::Top:
|
||||
break;
|
||||
case Qgis::TextVerticalAlignment::VerticalCenter:
|
||||
component.origin.ry() += ( rect.height() - metrics.documentSize( mode ).height() ) / 2;
|
||||
component.origin.ry() += ( rect.height() - metrics.documentSize( mode, format.orientation() ).height() ) / 2;
|
||||
break;
|
||||
case Qgis::TextVerticalAlignment::Bottom:
|
||||
component.origin.ry() += ( rect.height() - metrics.documentSize( mode ).height() );
|
||||
component.origin.ry() += ( rect.height() - metrics.documentSize( mode, format.orientation() ).height() );
|
||||
break;
|
||||
}
|
||||
|
||||
@ -304,6 +304,7 @@ QFontMetricsF QgsTextRenderer::fontMetrics( QgsRenderContext &context, const Qgs
|
||||
}
|
||||
|
||||
double QgsTextRenderer::drawBuffer( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format,
|
||||
const QgsTextDocumentMetrics &metrics,
|
||||
Qgis::TextLayoutMode mode )
|
||||
{
|
||||
QPainter *p = context.painter();
|
||||
@ -351,6 +352,7 @@ double QgsTextRenderer::drawBuffer( QgsRenderContext &context, const QgsTextRend
|
||||
QPainterPath path;
|
||||
path.setFillRule( Qt::WindingFill );
|
||||
double advance = 0;
|
||||
double height = component.size.height();
|
||||
switch ( orientation )
|
||||
{
|
||||
case Qgis::TextOrientation::Horizontal:
|
||||
@ -375,24 +377,30 @@ double QgsTextRenderer::drawBuffer( QgsRenderContext &context, const QgsTextRend
|
||||
case Qgis::TextOrientation::Vertical:
|
||||
case Qgis::TextOrientation::RotationBased:
|
||||
{
|
||||
double letterSpacing = font.letterSpacing();
|
||||
double partYOffset = component.offset.y() * scaleFactor;
|
||||
|
||||
const double blockMaximumCharacterWidth = metrics.blockMaximumCharacterWidth( component.blockIndex );
|
||||
double partLastDescent = 0;
|
||||
|
||||
for ( const QgsTextFragment &fragment : component.block )
|
||||
{
|
||||
QFont fragmentFont = font;
|
||||
fragment.characterFormat().updateFontForFormat( fragmentFont, context, scaleFactor );
|
||||
const double letterSpacing = fragmentFont.letterSpacing() / scaleFactor;
|
||||
|
||||
QFontMetricsF fragmentMetrics( fragmentFont );
|
||||
const double labelWidth = fragmentMetrics.maxWidth();
|
||||
const QFontMetricsF fragmentMetrics( fragmentFont );
|
||||
|
||||
const QStringList parts = QgsPalLabeling::splitToGraphemes( fragment.text() );
|
||||
for ( const QString &part : parts )
|
||||
{
|
||||
double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) - letterSpacing ) ) / 2;
|
||||
double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / scaleFactor - letterSpacing ) ) / 2;
|
||||
partYOffset += fragmentMetrics.ascent() / scaleFactor;
|
||||
path.addText( partXOffset, partYOffset, fragmentFont, part );
|
||||
partYOffset += fragmentMetrics.ascent() + letterSpacing;
|
||||
partYOffset += letterSpacing;
|
||||
}
|
||||
partLastDescent = fragmentMetrics.descent() / scaleFactor;
|
||||
}
|
||||
height = partYOffset + partLastDescent;
|
||||
advance = partYOffset - component.offset.y() * scaleFactor;
|
||||
break;
|
||||
}
|
||||
@ -447,10 +455,11 @@ double QgsTextRenderer::drawBuffer( QgsRenderContext &context, const QgsTextRend
|
||||
bufferComponent.origin = QPointF( 0.0, 0.0 );
|
||||
bufferComponent.picture = buffPict;
|
||||
bufferComponent.pictureBuffer = penSize / 2.0;
|
||||
bufferComponent.size.setHeight( height );
|
||||
|
||||
if ( format.orientation() == Qgis::TextOrientation::Vertical || format.orientation() == Qgis::TextOrientation::RotationBased )
|
||||
{
|
||||
bufferComponent.offset.setY( bufferComponent.offset.y() - bufferComponent.size.height() );
|
||||
bufferComponent.offset.setY( - bufferComponent.size.height() );
|
||||
}
|
||||
drawShadow( context, bufferComponent, format );
|
||||
}
|
||||
@ -588,52 +597,8 @@ double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTex
|
||||
|
||||
const QgsTextDocumentMetrics metrics = QgsTextDocumentMetrics::calculateMetrics( document, format, context, scaleFactor );
|
||||
|
||||
double width = 0;
|
||||
switch ( format.orientation() )
|
||||
{
|
||||
case Qgis::TextOrientation::Horizontal:
|
||||
{
|
||||
// width doesn't change depending on layout mode, we can use anything here
|
||||
width = metrics.documentSize( Qgis::TextLayoutMode::Point ).width();
|
||||
break;
|
||||
}
|
||||
|
||||
case Qgis::TextOrientation::Vertical:
|
||||
{
|
||||
double totalLineWidth = 0;
|
||||
int blockIndex = 0;
|
||||
|
||||
bool isNullSize = false;
|
||||
const QFont baseFont = format.scaledFont( context, scaleFactor, &isNullSize );
|
||||
if ( isNullSize )
|
||||
return 0;
|
||||
|
||||
const double lineHeightPainterUnits = context.convertToPainterUnits( format.lineHeight(), format.lineHeightUnit() );
|
||||
for ( const QgsTextBlock &block : document )
|
||||
{
|
||||
double blockWidth = 0;
|
||||
for ( const QgsTextFragment &fragment : block )
|
||||
{
|
||||
QFont fragmentFont = baseFont;
|
||||
fragment.characterFormat().updateFontForFormat( fragmentFont, context, scaleFactor );
|
||||
blockWidth = std::max( QFontMetricsF( fragmentFont ).maxWidth(), blockWidth );
|
||||
}
|
||||
|
||||
totalLineWidth += blockIndex == 0 ? blockWidth : ( format.lineHeightUnit() == QgsUnitTypes::RenderPercentage ? ( blockWidth * format.lineHeight() ) : lineHeightPainterUnits );
|
||||
blockIndex++;
|
||||
}
|
||||
width = totalLineWidth / scaleFactor;
|
||||
break;
|
||||
}
|
||||
|
||||
case Qgis::TextOrientation::RotationBased:
|
||||
{
|
||||
// label mode only
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return width;
|
||||
// width doesn't change depending on layout mode, we can use anything here
|
||||
return metrics.documentSize( Qgis::TextLayoutMode::Point, format.orientation() ).width();
|
||||
}
|
||||
|
||||
double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, Qgis::TextLayoutMode mode, QFontMetricsF *, Qgis::TextRendererFlags flags, double maxLineWidth )
|
||||
@ -795,48 +760,7 @@ double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTe
|
||||
return 0;
|
||||
|
||||
const QgsTextDocumentMetrics metrics = QgsTextDocumentMetrics::calculateMetrics( document, format, context, scaleFactor );
|
||||
|
||||
switch ( format.orientation() )
|
||||
{
|
||||
case Qgis::TextOrientation::Horizontal:
|
||||
{
|
||||
return metrics.documentSize( mode ).height();
|
||||
}
|
||||
|
||||
case Qgis::TextOrientation::Vertical:
|
||||
{
|
||||
double maxBlockHeight = 0;
|
||||
for ( const QgsTextBlock &block : document )
|
||||
{
|
||||
double blockHeight = 0;
|
||||
int fragmentIndex = 0;
|
||||
for ( const QgsTextFragment &fragment : block )
|
||||
{
|
||||
QFont fragmentFont = baseFont;
|
||||
fragment.characterFormat().updateFontForFormat( fragmentFont, context, scaleFactor );
|
||||
const QFontMetricsF fm( fragmentFont );
|
||||
|
||||
const double labelHeight = fm.ascent();
|
||||
const double letterSpacing = fragmentFont.letterSpacing();
|
||||
|
||||
blockHeight += fragmentIndex = 0 ? labelHeight * fragment.text().size() + ( fragment.text().size() - 1 ) * letterSpacing
|
||||
: fragment.text().size() * ( labelHeight + letterSpacing );
|
||||
fragmentIndex++;
|
||||
}
|
||||
maxBlockHeight = std::max( maxBlockHeight, blockHeight );
|
||||
}
|
||||
|
||||
return maxBlockHeight / scaleFactor;
|
||||
}
|
||||
|
||||
case Qgis::TextOrientation::RotationBased:
|
||||
{
|
||||
// label mode only
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return metrics.documentSize( mode, format.orientation() ).height();
|
||||
}
|
||||
|
||||
void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer::Component component, const QgsTextFormat &format, const QgsTextDocumentMetrics &metrics, Qgis::TextLayoutMode mode )
|
||||
@ -876,7 +800,7 @@ void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer
|
||||
if ( mode != Qgis::TextLayoutMode::Labeling )
|
||||
{
|
||||
// need to calculate size of text
|
||||
const QSizeF documentSize = metrics.documentSize( mode );
|
||||
const QSizeF documentSize = metrics.documentSize( mode, format.orientation() );
|
||||
double width = documentSize.width();
|
||||
double height = documentSize.height();
|
||||
|
||||
@ -1242,7 +1166,8 @@ void QgsTextRenderer::drawShadow( QgsRenderContext &context, const QgsTextRender
|
||||
// then scale device painter by 1.0 / rasterCompressFactor for output
|
||||
|
||||
QPainter *p = context.painter();
|
||||
double componentWidth = component.size.width(), componentHeight = component.size.height();
|
||||
const double componentWidth = component.size.width();
|
||||
const double componentHeight = component.size.height();
|
||||
double xOffset = component.offset.x(), yOffset = component.offset.y();
|
||||
double pictbuffer = component.pictureBuffer;
|
||||
|
||||
@ -1432,7 +1357,7 @@ void QgsTextRenderer::drawTextInternal( Qgis::TextComponent drawType,
|
||||
case Qgis::TextOrientation::Vertical:
|
||||
case Qgis::TextOrientation::RotationBased:
|
||||
{
|
||||
drawTextInternalVertical( context, format, drawType, mode, component, document, fontScale, baseFont, alignment, vAlignment, rotation );
|
||||
drawTextInternalVertical( context, format, drawType, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1519,7 +1444,7 @@ void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, con
|
||||
QPainter *maskPainter = context.maskPainter( context.currentMaskId() );
|
||||
const QStringList textLines = document.toPlainText();
|
||||
|
||||
const QSizeF documentSize = metrics.documentSize( mode );
|
||||
const QSizeF documentSize = metrics.documentSize( mode, Qgis::TextOrientation::Horizontal );
|
||||
|
||||
double labelWidest = 0.0;
|
||||
switch ( mode )
|
||||
@ -1645,6 +1570,7 @@ void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, con
|
||||
|
||||
Component subComponent;
|
||||
subComponent.block = block;
|
||||
subComponent.blockIndex = blockIndex;
|
||||
subComponent.size = QSizeF( blockWidth, blockHeight );
|
||||
subComponent.offset = QPointF( 0.0, -metrics.ascentOffset() );
|
||||
subComponent.rotation = -component.rotation * 180 / M_PI;
|
||||
@ -1660,7 +1586,7 @@ void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, con
|
||||
|
||||
if ( drawType == Qgis::TextComponent::Buffer )
|
||||
{
|
||||
QgsTextRenderer::drawBuffer( context, subComponent, format, mode );
|
||||
QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1772,7 +1698,7 @@ void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, con
|
||||
}
|
||||
}
|
||||
|
||||
void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const QgsTextFormat &format, Qgis::TextComponent drawType, Qgis::TextLayoutMode mode, const QgsTextRenderer::Component &component, const QgsTextDocument &document, double fontScale, const QFont &baseFont, Qgis::TextHorizontalAlignment hAlignment, Qgis::TextVerticalAlignment, double rotation )
|
||||
void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const QgsTextFormat &format, Qgis::TextComponent drawType, Qgis::TextLayoutMode mode, const QgsTextRenderer::Component &component, const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics, double fontScale, Qgis::TextHorizontalAlignment hAlignment, Qgis::TextVerticalAlignment, double rotation )
|
||||
{
|
||||
QPainter *maskPainter = context.maskPainter( context.currentMaskId() );
|
||||
const QStringList textLines = document.toPlainText();
|
||||
@ -1792,23 +1718,19 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
|
||||
referenceScaleOverride.reset();
|
||||
|
||||
double letterSpacing = font.letterSpacing() / fontScale;
|
||||
const QSizeF documentSize = metrics.documentSize( mode, Qgis::TextOrientation::Vertical );
|
||||
const double actualTextWidth = documentSize.width();
|
||||
double textRectWidth = 0.0;
|
||||
|
||||
const QFontMetricsF fontMetrics( baseFont );
|
||||
const double lineHeightPainterUnits = context.convertToPainterUnits( format.lineHeight(), format.lineHeightUnit() );
|
||||
|
||||
double labelWidth = fontMetrics.maxWidth() / fontScale; // label width represents the width of one line of a multi-line label
|
||||
double actualLabelWidest = labelWidth + ( textLines.size() - 1 ) * ( format.lineHeightUnit() == QgsUnitTypes::RenderPercentage ? ( labelWidth * format.lineHeight() ) : lineHeightPainterUnits );
|
||||
double labelWidest = 0.0;
|
||||
switch ( mode )
|
||||
{
|
||||
case Qgis::TextLayoutMode::Labeling:
|
||||
case Qgis::TextLayoutMode::Point:
|
||||
labelWidest = actualLabelWidest;
|
||||
textRectWidth = actualTextWidth;
|
||||
break;
|
||||
|
||||
case Qgis::TextLayoutMode::Rectangle:
|
||||
labelWidest = component.size.width();
|
||||
textRectWidth = component.size.width();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1817,10 +1739,9 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
{
|
||||
maxLineLength = std::max( maxLineLength, static_cast<int>( line.length() ) );
|
||||
}
|
||||
double actualLabelHeight = fontMetrics.ascent() / fontScale + ( fontMetrics.ascent() / fontScale + letterSpacing ) * ( maxLineLength - 1 );
|
||||
double ascentOffset = fontMetrics.ascent() / fontScale;
|
||||
|
||||
int i = 0;
|
||||
const double actualLabelHeight = documentSize.height();
|
||||
int blockIndex = 0;
|
||||
|
||||
bool adjustForAlignment = hAlignment != Qgis::TextHorizontalAlignment::Left && ( mode != Qgis::TextLayoutMode::Labeling || textLines.size() > 1 );
|
||||
|
||||
@ -1842,19 +1763,21 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
maskPainter->rotate( rotation );
|
||||
}
|
||||
|
||||
const double blockMaximumCharacterWidth = metrics.blockMaximumCharacterWidth( blockIndex );
|
||||
|
||||
// figure x offset of multiple lines
|
||||
double xOffset = actualLabelWidest - labelWidth - i * ( format.lineHeightUnit() == QgsUnitTypes::RenderPercentage ? ( labelWidth * format.lineHeight() ) : lineHeightPainterUnits );
|
||||
double xOffset = metrics.verticalOrientationXOffset( blockIndex );
|
||||
if ( adjustForAlignment )
|
||||
{
|
||||
double labelWidthDiff = 0;
|
||||
double hAlignmentOffset = 0;
|
||||
switch ( hAlignment )
|
||||
{
|
||||
case Qgis::TextHorizontalAlignment::Center:
|
||||
labelWidthDiff = ( labelWidest - actualLabelWidest ) * 0.5;
|
||||
hAlignmentOffset = ( textRectWidth - actualTextWidth ) * 0.5;
|
||||
break;
|
||||
|
||||
case Qgis::TextHorizontalAlignment::Right:
|
||||
labelWidthDiff = labelWidest - actualLabelWidest;
|
||||
hAlignmentOffset = textRectWidth - actualTextWidth;
|
||||
break;
|
||||
|
||||
case Qgis::TextHorizontalAlignment::Left:
|
||||
@ -1866,7 +1789,7 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
{
|
||||
case Qgis::TextLayoutMode::Labeling:
|
||||
case Qgis::TextLayoutMode::Rectangle:
|
||||
xOffset += labelWidthDiff;
|
||||
xOffset += hAlignmentOffset;
|
||||
break;
|
||||
|
||||
case Qgis::TextLayoutMode::Point:
|
||||
@ -1882,34 +1805,37 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
{
|
||||
if ( rotation >= -405 && rotation < -180 )
|
||||
{
|
||||
yOffset = ascentOffset;
|
||||
yOffset = 0;
|
||||
}
|
||||
else if ( rotation >= 0 && rotation < 45 )
|
||||
{
|
||||
xOffset -= actualLabelWidest;
|
||||
yOffset = -actualLabelHeight + ascentOffset + fontMetrics.descent() / fontScale;
|
||||
xOffset -= actualTextWidth;
|
||||
yOffset = -actualLabelHeight + metrics.blockMaximumDescent( blockIndex );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yOffset = -actualLabelHeight + ascentOffset;
|
||||
yOffset = -actualLabelHeight;
|
||||
}
|
||||
break;
|
||||
|
||||
case Qgis::TextLayoutMode::Point:
|
||||
yOffset = -actualLabelHeight + ascentOffset;
|
||||
yOffset = -actualLabelHeight;
|
||||
break;
|
||||
|
||||
case Qgis::TextLayoutMode::Rectangle:
|
||||
yOffset = ascentOffset;
|
||||
yOffset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
context.painter()->translate( QPointF( xOffset, yOffset ) );
|
||||
|
||||
double fragmentYOffset = 0;
|
||||
double currentBlockYOffset = 0;
|
||||
int fragmentIndex = 0;
|
||||
for ( const QgsTextFragment &fragment : block )
|
||||
{
|
||||
QgsScopedQPainterState fragmentPainterState( context.painter() );
|
||||
|
||||
// apply some character replacement to draw symbols in vertical presentation
|
||||
const QString line = QgsStringUtils::substituteVerticalCharacters( fragment.text() );
|
||||
|
||||
@ -1918,12 +1844,14 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
|
||||
QFontMetricsF fragmentMetrics( fragmentFont );
|
||||
|
||||
double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
|
||||
const double letterSpacing = fragmentFont.letterSpacing() / fontScale;
|
||||
const double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
|
||||
|
||||
Component subComponent;
|
||||
subComponent.block = QgsTextBlock( fragment );
|
||||
subComponent.size = QSizeF( labelWidth, labelHeight );
|
||||
subComponent.offset = QPointF( 0.0, fragmentYOffset );
|
||||
subComponent.blockIndex = blockIndex;
|
||||
subComponent.size = QSizeF( blockMaximumCharacterWidth, labelHeight + fragmentMetrics.descent() / fontScale );
|
||||
subComponent.offset = QPointF( 0.0, currentBlockYOffset );
|
||||
subComponent.rotation = -component.rotation * 180 / M_PI;
|
||||
subComponent.rotationOffset = 0.0;
|
||||
|
||||
@ -1938,7 +1866,7 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
|
||||
if ( drawType == Qgis::TextComponent::Buffer )
|
||||
{
|
||||
fragmentYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format, mode );
|
||||
currentBlockYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1947,11 +1875,12 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
path.setFillRule( Qt::WindingFill );
|
||||
const QStringList parts = QgsPalLabeling::splitToGraphemes( fragment.text() );
|
||||
double partYOffset = 0.0;
|
||||
for ( const auto &part : parts )
|
||||
for ( const QString &part : parts )
|
||||
{
|
||||
double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
|
||||
double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
|
||||
partYOffset += fragmentMetrics.ascent() / fontScale;
|
||||
path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
|
||||
partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
|
||||
partYOffset += letterSpacing;
|
||||
}
|
||||
|
||||
// store text's drawing in QPicture for drop shadow call
|
||||
@ -1964,6 +1893,7 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
textp.setBrush( textColor );
|
||||
textp.scale( 1 / fontScale, 1 / fontScale );
|
||||
textp.drawPath( path );
|
||||
|
||||
// TODO: why are some font settings lost on drawPicture() when using drawText() inside QPicture?
|
||||
// e.g. some capitalization options, but not others
|
||||
//textp.setFont( tmpLyr.textFont );
|
||||
@ -1975,9 +1905,9 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
{
|
||||
subComponent.picture = textPict;
|
||||
subComponent.pictureBuffer = 0.0; // no pen width to deal with
|
||||
subComponent.origin = QPointF( 0.0, fragmentYOffset );
|
||||
subComponent.origin = QPointF( 0.0, currentBlockYOffset );
|
||||
const double prevY = subComponent.offset.y();
|
||||
subComponent.offset = QPointF( 0, -labelHeight );
|
||||
subComponent.offset = QPointF( 0, -subComponent.size.height() );
|
||||
subComponent.useOrigin = true;
|
||||
QgsTextRenderer::drawShadow( context, subComponent, format );
|
||||
subComponent.useOrigin = false;
|
||||
@ -1998,9 +1928,10 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
case Qgis::TextRenderFormat::AlwaysOutlines:
|
||||
{
|
||||
// draw outlined text
|
||||
context.painter()->translate( 0, currentBlockYOffset );
|
||||
_fixQPictureDPI( context.painter() );
|
||||
context.painter()->drawPicture( 0, fragmentYOffset, textPict );
|
||||
fragmentYOffset += partYOffset;
|
||||
context.painter()->drawPicture( 0, 0, textPict );
|
||||
currentBlockYOffset += partYOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2013,21 +1944,22 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
|
||||
double partYOffset = 0.0;
|
||||
for ( const QString &part : parts )
|
||||
{
|
||||
double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
|
||||
double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
|
||||
context.painter()->scale( 1 / fontScale, 1 / fontScale );
|
||||
context.painter()->drawText( partXOffset * fontScale, ( fragmentYOffset + partYOffset ) * fontScale, part );
|
||||
context.painter()->drawText( partXOffset * fontScale, ( currentBlockYOffset + partYOffset ) * fontScale, part );
|
||||
context.painter()->scale( fontScale, fontScale );
|
||||
partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
|
||||
}
|
||||
fragmentYOffset += partYOffset;
|
||||
currentBlockYOffset += partYOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
fragmentIndex++;
|
||||
}
|
||||
|
||||
if ( maskPainter )
|
||||
maskPainter->restore();
|
||||
i++;
|
||||
blockIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,6 +234,10 @@ class CORE_EXPORT QgsTextRenderer
|
||||
{
|
||||
//! Block to render
|
||||
QgsTextBlock block;
|
||||
|
||||
//! Index of block
|
||||
int blockIndex = 0;
|
||||
|
||||
//! Current origin point for painting (generally current painter rotation point)
|
||||
QPointF origin;
|
||||
//! Whether to translate the painter to supplied origin
|
||||
@ -315,6 +319,7 @@ class CORE_EXPORT QgsTextRenderer
|
||||
static double drawBuffer( QgsRenderContext &context,
|
||||
const Component &component,
|
||||
const QgsTextFormat &format,
|
||||
const QgsTextDocumentMetrics &metrics,
|
||||
Qgis::TextLayoutMode mode );
|
||||
|
||||
static void drawBackground( QgsRenderContext &context,
|
||||
@ -369,8 +374,8 @@ class CORE_EXPORT QgsTextRenderer
|
||||
Qgis::TextLayoutMode mode,
|
||||
const Component &component,
|
||||
const QgsTextDocument &document,
|
||||
const QgsTextDocumentMetrics &metrics,
|
||||
double fontScale,
|
||||
const QFont &baseFont,
|
||||
Qgis::TextHorizontalAlignment hAlignment,
|
||||
Qgis::TextVerticalAlignment vAlignment,
|
||||
double rotation );
|
||||
|
Loading…
x
Reference in New Issue
Block a user