Skip to content

Commit 5092939

Browse files
committedMay 12, 2020
Update QgsTextRenderer::textWidth/textHeight to handle HTML formatting
1 parent 8b26316 commit 5092939

File tree

5 files changed

+119
-55
lines changed

5 files changed

+119
-55
lines changed
 

‎src/app/decorations/qgsdecorationcopyright.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,10 @@ void QgsDecorationCopyright::render( const QgsMapSettings &mapSettings, QgsRende
121121
QString displayString = QgsExpression::replaceExpressionText( mLabelText, &context.expressionContext() );
122122
QStringList displayStringList = displayString.split( '\n' );
123123

124-
QFontMetricsF fm( mTextFormat.scaledFont( context ) );
125124
QFontMetricsF textMetrics = QgsTextRenderer::fontMetrics( context, mTextFormat );
126125
double textDescent = textMetrics.descent();
127-
double textWidth = QgsTextRenderer::textWidth( context, mTextFormat, displayStringList, &fm );
128-
double textHeight = QgsTextRenderer::textHeight( context, mTextFormat, displayStringList, QgsTextRenderer::Point, &fm );
126+
double textWidth = QgsTextRenderer::textWidth( context, mTextFormat, displayStringList );
127+
double textHeight = QgsTextRenderer::textHeight( context, mTextFormat, displayStringList, QgsTextRenderer::Point );
129128

130129
QPaintDevice *device = context.painter()->device();
131130
int deviceHeight = device->height() / device->devicePixelRatioF();

‎src/app/decorations/qgsdecorationtitle.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,10 @@ void QgsDecorationTitle::render( const QgsMapSettings &mapSettings, QgsRenderCon
110110
QString displayString = QgsExpression::replaceExpressionText( mLabelText, &context.expressionContext() );
111111
QStringList displayStringList = displayString.split( '\n' );
112112

113-
QFontMetricsF fm( mTextFormat.scaledFont( context ) );
114113
QFontMetricsF textMetrics = QgsTextRenderer::fontMetrics( context, mTextFormat );
115114
double textDescent = textMetrics.descent();
116-
double textWidth = QgsTextRenderer::textWidth( context, mTextFormat, displayStringList, &fm );
117-
double textHeight = QgsTextRenderer::textHeight( context, mTextFormat, displayStringList, QgsTextRenderer::Point, &fm );
115+
double textWidth = QgsTextRenderer::textWidth( context, mTextFormat, displayStringList );
116+
double textHeight = QgsTextRenderer::textHeight( context, mTextFormat, displayStringList, QgsTextRenderer::Point );
118117

119118
QPaintDevice *device = context.painter()->device();
120119
int deviceHeight = device->height() / device->devicePixelRatioF();

‎src/core/labeling/qgsvectorlayerlabelprovider.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -520,8 +520,6 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, Q
520520
component.origin = outPt;
521521
component.rotation = label->getAlpha();
522522

523-
524-
525523
if ( drawType == QgsTextRenderer::Background )
526524
{
527525
// get rotated label's center point
@@ -547,7 +545,7 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, Q
547545

548546
component.size = QSizeF( labelWidthPx, labelHeightPx );
549547

550-
QgsTextRenderer::drawBackground( context, component, tmpLyr.format(), QStringList(), QgsTextRenderer::Label );
548+
QgsTextRenderer::drawBackground( context, component, tmpLyr.format(), QgsTextDocument(), QgsTextRenderer::Label );
551549
}
552550

553551
else if ( drawType == QgsTextRenderer::Buffer

‎src/core/textrenderer/qgstextrenderer.cpp

Lines changed: 110 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,7 @@ void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, QgsTextRend
154154
component.center = rect.center();
155155
}
156156

157-
const QStringList lines = document.toPlainText();
158-
QgsTextRenderer::drawBackground( context, component, format, lines, Rect );
157+
QgsTextRenderer::drawBackground( context, component, format, document, Rect );
159158

160159
break;
161160
}
@@ -205,8 +204,7 @@ void QgsTextRenderer::drawPart( QPointF origin, double rotation, QgsTextRenderer
205204
if ( !format.background().enabled() )
206205
return;
207206

208-
const QStringList lines = document.toPlainText();
209-
QgsTextRenderer::drawBackground( context, component, format, lines, Point );
207+
QgsTextRenderer::drawBackground( context, component, format, document, Point );
210208
break;
211209
}
212210

@@ -447,35 +445,60 @@ void QgsTextRenderer::drawMask( QgsRenderContext &context, const QgsTextRenderer
447445
p->restore();
448446
}
449447

450-
double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics )
448+
double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF * )
451449
{
452-
//calculate max width of text lines
453-
std::unique_ptr< QFontMetricsF > newFm;
454-
if ( !fontMetrics )
450+
if ( !format.allowHtmlFormatting() )
455451
{
456-
newFm.reset( new QFontMetricsF( format.scaledFont( context ) ) );
457-
fontMetrics = newFm.get();
452+
return textWidth( context, format, QgsTextDocument::fromPlainText( textLines ) );
458453
}
454+
else
455+
{
456+
return textWidth( context, format, QgsTextDocument::fromHtml( textLines ) );
457+
}
458+
}
459+
460+
double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &document )
461+
{
462+
//calculate max width of text lines
463+
const QFont baseFont = format.scaledFont( context );
459464

460465
double width = 0;
461466
switch ( format.orientation() )
462467
{
463468
case QgsTextFormat::HorizontalOrientation:
464469
{
465470
double maxLineWidth = 0;
466-
const auto constTextLines = textLines;
467-
for ( const QString &line : constTextLines )
471+
for ( const QgsTextBlock &block : document )
468472
{
469-
maxLineWidth = std::max( maxLineWidth, fontMetrics->width( line ) );
473+
double blockWidth = 0;
474+
for ( const QgsTextFragment &fragment : block )
475+
{
476+
blockWidth += fragment.horizontalAdvance( baseFont );
477+
}
478+
maxLineWidth = std::max( maxLineWidth, blockWidth );
470479
}
471480
width = maxLineWidth;
472481
break;
473482
}
474483

475484
case QgsTextFormat::VerticalOrientation:
476485
{
477-
double labelWidth = fontMetrics->maxWidth();
478-
width = labelWidth + ( textLines.size() - 1 ) * labelWidth * format.lineHeight();
486+
double totalLineWidth = 0;
487+
int blockIndex = 0;
488+
for ( const QgsTextBlock &block : document )
489+
{
490+
double blockWidth = 0;
491+
for ( const QgsTextFragment &fragment : block )
492+
{
493+
QFont fragmentFont = baseFont;
494+
fragment.characterFormat().updateFontForFormat( fragmentFont );
495+
blockWidth = std::max( QFontMetricsF( fragmentFont ).maxWidth(), blockWidth );
496+
}
497+
498+
totalLineWidth += blockIndex == 0 ? blockWidth : blockWidth * format.lineHeight();
499+
blockIndex++;
500+
}
501+
width = totalLineWidth;
479502
break;
480503
}
481504

@@ -489,49 +512,92 @@ double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTex
489512
return width;
490513
}
491514

492-
double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode, QFontMetricsF *fontMetrics )
515+
double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode, QFontMetricsF * )
493516
{
494-
//calculate max width of text lines
495-
std::unique_ptr< QFontMetricsF > newFm;
496-
if ( !fontMetrics )
517+
if ( !format.allowHtmlFormatting() )
497518
{
498-
newFm.reset( new QFontMetricsF( format.scaledFont( context ) ) );
499-
fontMetrics = newFm.get();
519+
return textHeight( context, format, QgsTextDocument::fromPlainText( textLines ), mode );
500520
}
521+
else
522+
{
523+
return textHeight( context, format, QgsTextDocument::fromHtml( textLines ), mode );
524+
}
525+
}
526+
527+
double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &document, DrawMode mode )
528+
{
529+
//calculate max height of text lines
530+
531+
const QFont baseFont = format.scaledFont( context );
501532

502533
switch ( format.orientation() )
503534
{
504535
case QgsTextFormat::HorizontalOrientation:
505536
{
506-
double labelHeight = fontMetrics->ascent() + fontMetrics->descent(); // ignore +1 for baseline
507-
switch ( mode )
537+
int blockIndex = 0;
538+
double totalHeight = 0;
539+
for ( const QgsTextBlock &block : document )
508540
{
509-
case Label:
510-
// rendering labels needs special handling - in this case text should be
511-
// drawn with the bottom left corner coinciding with origin, vs top left
512-
// for standard text rendering. Line height is also slightly different.
513-
return labelHeight + ( textLines.size() - 1 ) * labelHeight * format.lineHeight();
541+
double maxBlockHeight = 0;
542+
double maxBlockLineSpacing = 0;
543+
for ( const QgsTextFragment &fragment : block )
544+
{
545+
QFont fragmentFont = baseFont;
546+
fragment.characterFormat().updateFontForFormat( fragmentFont );
547+
const QFontMetricsF fm( fragmentFont );
514548

515-
case Rect:
516-
case Point:
517-
// standard rendering - designed to exactly replicate QPainter's drawText method
518-
return labelHeight + ( textLines.size() - 1 ) * fontMetrics->lineSpacing() * format.lineHeight();
549+
const double fragmentHeight = fm.ascent() + fm.descent(); // ignore +1 for baseline
550+
551+
maxBlockHeight = std::max( maxBlockHeight, fragmentHeight );
552+
maxBlockLineSpacing = std::max( maxBlockLineSpacing, fm.lineSpacing() );
553+
}
554+
555+
switch ( mode )
556+
{
557+
case Label:
558+
// rendering labels needs special handling - in this case text should be
559+
// drawn with the bottom left corner coinciding with origin, vs top left
560+
// for standard text rendering. Line height is also slightly different.
561+
totalHeight += blockIndex == 0 ? maxBlockHeight : maxBlockHeight * format.lineHeight();
562+
break;
563+
564+
case Rect:
565+
case Point:
566+
// standard rendering - designed to exactly replicate QPainter's drawText method
567+
totalHeight += blockIndex == 0 ? maxBlockHeight : maxBlockLineSpacing * format.lineHeight();
568+
break;
569+
}
570+
571+
blockIndex++;
519572
}
520-
break;
573+
574+
return totalHeight;
521575
}
522576

523577
case QgsTextFormat::VerticalOrientation:
524578
{
525-
double labelHeight = fontMetrics->ascent();
526-
double letterSpacing = format.scaledFont( context ).letterSpacing();
527-
int maxLineLength = 0;
528-
for ( const auto &line : textLines )
579+
double maxBlockHeight = 0;
580+
for ( const QgsTextBlock &block : document )
529581
{
530-
if ( line.length() > maxLineLength )
531-
maxLineLength = line.length();
582+
double blockHeight = 0;
583+
int fragmentIndex = 0;
584+
for ( const QgsTextFragment &fragment : block )
585+
{
586+
QFont fragmentFont = baseFont;
587+
fragment.characterFormat().updateFontForFormat( fragmentFont );
588+
const QFontMetricsF fm( fragmentFont );
589+
590+
const double labelHeight = fm.ascent();
591+
const double letterSpacing = fragmentFont.letterSpacing();
592+
593+
blockHeight += fragmentIndex = 0 ? labelHeight * fragment.text().size() + ( fragment.text().size() - 1 ) * letterSpacing
594+
: fragment.text().size() * ( labelHeight + letterSpacing );
595+
fragmentIndex++;
596+
}
597+
maxBlockHeight = std::max( maxBlockHeight, blockHeight );
532598
}
533-
return labelHeight * maxLineLength + ( maxLineLength - 1 ) * letterSpacing;
534-
break;
599+
600+
return maxBlockHeight;
535601
}
536602

537603
case QgsTextFormat::RotationBasedOrientation:
@@ -544,8 +610,7 @@ double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTe
544610
return 0;
545611
}
546612

547-
void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer::Component component, const QgsTextFormat &format,
548-
const QStringList &textLines, DrawMode mode )
613+
void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer::Component component, const QgsTextFormat &format, const QgsTextDocument &document, QgsTextRenderer::DrawMode mode )
549614
{
550615
QgsTextBackgroundSettings background = format.background();
551616

@@ -578,8 +643,8 @@ void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer
578643
{
579644
// need to calculate size of text
580645
QFontMetricsF fm( format.scaledFont( context ) );
581-
double width = textWidth( context, format, textLines, &fm );
582-
double height = textHeight( context, format, textLines, mode, &fm );
646+
double width = textWidth( context, format, document );
647+
double height = textHeight( context, format, document, mode );
583648

584649
switch ( mode )
585650
{

‎src/core/textrenderer/qgstextrenderer.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ class CORE_EXPORT QgsTextRenderer
215215
HAlignment hAlign = AlignLeft;
216216
};
217217

218+
static double textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &document );
219+
static double textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &document, DrawMode mode );
220+
218221
/**
219222
* Draws a single component of rendered text using the specified settings.
220223
* \param rect destination rectangle for text
@@ -258,7 +261,7 @@ class CORE_EXPORT QgsTextRenderer
258261
static void drawBackground( QgsRenderContext &context,
259262
Component component,
260263
const QgsTextFormat &format,
261-
const QStringList &textLines,
264+
const QgsTextDocument &document,
262265
DrawMode mode = Rect );
263266

264267
static void drawShadow( QgsRenderContext &context,

0 commit comments

Comments
 (0)
Please sign in to comment.