Skip to content

Commit a7c39ff

Browse files
committedNov 14, 2022
Add support for super and subscript HTML formatting in text renderer
This allows for either: - <sup>superscript</sup> / <sub>subscript</sub> components in text, where the text will be vertically super or subscript aligned and automatically sized to 2/3rd of the parent font size. Users can also set a fixed font size for the super/sub script by including css rules, e.g. <sup style="font-size:33pt">super</sup> - "vertical-align: super" or "vertical-align: sub" CSS formatting rules in any other HTML element Sponsored by OSGEO UK
1 parent 79b809a commit a7c39ff

File tree

13 files changed

+340
-15
lines changed

13 files changed

+340
-15
lines changed
 

‎python/core/auto_generated/textrenderer/qgstextdocumentmetrics.sip.in

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ Returns ``True`` if the metrics could not be calculated because the text format
4949
QSizeF documentSize( Qgis::TextLayoutMode mode, Qgis::TextOrientation orientation ) const;
5050
%Docstring
5151
Returns the overall size of the document.
52+
%End
53+
54+
QRectF outerBounds( Qgis::TextLayoutMode mode, Qgis::TextOrientation orientation ) const;
55+
%Docstring
56+
Returns the outer bounds of the document, which is the :py:func:`~QgsTextDocumentMetrics.documentSize` adjusted to account
57+
for any text elements which fall outside of the usual document margins (such as super or
58+
sub script elements)
59+
60+
.. warning::
61+
62+
Currently this is only supported for the Qgis.TextLayoutMode.Labeling mode.
63+
64+
.. versionadded:: 3.30
5265
%End
5366

5467
double blockWidth( int blockIndex ) const;
@@ -70,6 +83,14 @@ Returns the offset from the top of the document to the text baseline for the giv
7083
%Docstring
7184
Returns the horizontal advance of the fragment at the specified block and fragment index.
7285

86+
.. versionadded:: 3.30
87+
%End
88+
89+
double fragmentVerticalOffset( int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode ) const;
90+
%Docstring
91+
Returns the vertical offset from a text block's baseline which should be applied
92+
to the fragment at the specified index within that block.
93+
7394
.. versionadded:: 3.30
7495
%End
7596

‎src/core/labeling/qgspallabeling.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,7 +1460,7 @@ bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext &ct, const
14601460
return QgsPalLabeling::checkMinimumSizeMM( ct, geom, minSize );
14611461
}
14621462

1463-
void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, const QString &text, double &labelX, double &labelY, const QgsFeature *f, QgsRenderContext *context, double *rotatedLabelX, double *rotatedLabelY, QgsTextDocument *document, QgsTextDocumentMetrics *documentMetrics )
1463+
void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, const QString &text, double &labelX, double &labelY, const QgsFeature *f, QgsRenderContext *context, double *rotatedLabelX, double *rotatedLabelY, QgsTextDocument *document, QgsTextDocumentMetrics *documentMetrics, QRectF *outerBounds )
14641464
{
14651465
if ( !fm || !f )
14661466
{
@@ -1714,6 +1714,16 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, const QSt
17141714
*rotatedLabelY = rh * uPP;
17151715
}
17161716
#endif
1717+
1718+
if ( outerBounds && documentMetrics )
1719+
{
1720+
const QRectF outerBoundsPixels = documentMetrics->outerBounds( Qgis::TextLayoutMode::Labeling, orientation );
1721+
1722+
*outerBounds = QRectF( outerBoundsPixels.left() * uPP,
1723+
outerBoundsPixels.top() * uPP,
1724+
outerBoundsPixels.width() * uPP,
1725+
outerBoundsPixels.height() * uPP );
1726+
}
17171727
}
17181728

17191729
void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext &context )
@@ -2018,15 +2028,16 @@ std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerFeatureWithDetails
20182028

20192029
QgsTextDocument doc;
20202030
QgsTextDocumentMetrics documentMetrics;
2031+
QRectF outerBounds;
20212032
if ( format().allowHtmlFormatting() && !labelText.isEmpty() )
20222033
{
20232034
doc = QgsTextDocument::fromHtml( QStringList() << labelText );
20242035
// also applies the line split to doc and calculates document metrics!
2025-
calculateLabelSize( labelFontMetrics.get(), labelText, labelWidth, labelHeight, mCurFeat, &context, &rotatedLabelX, &rotatedLabelY, &doc, &documentMetrics );
2036+
calculateLabelSize( labelFontMetrics.get(), labelText, labelWidth, labelHeight, mCurFeat, &context, &rotatedLabelX, &rotatedLabelY, &doc, &documentMetrics, &outerBounds );
20262037
}
20272038
else
20282039
{
2029-
calculateLabelSize( labelFontMetrics.get(), labelText, labelWidth, labelHeight, mCurFeat, &context, &rotatedLabelX, &rotatedLabelY, nullptr, nullptr );
2040+
calculateLabelSize( labelFontMetrics.get(), labelText, labelWidth, labelHeight, mCurFeat, &context, &rotatedLabelX, &rotatedLabelY, nullptr, nullptr, &outerBounds );
20302041
}
20312042

20322043
// maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
@@ -2668,6 +2679,11 @@ std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerFeatureWithDetails
26682679
obstacleGeometry.boundingBox().height() ) );
26692680
}
26702681

2682+
if ( outerBounds.left() != 0 || outerBounds.top() != 0 || !qgsDoubleNear( outerBounds.width(), labelWidth ) || !qgsDoubleNear( outerBounds.height(), labelHeight ) )
2683+
{
2684+
labelFeature->setOuterBounds( outerBounds );
2685+
}
2686+
26712687
//set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent
26722688
//this makes labels align to the font's baseline or highest character
26732689
double topMargin = std::max( 0.25 * labelFontMetrics->ascent(), 0.0 );

‎src/core/labeling/qgspallabeling.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,7 @@ class CORE_EXPORT QgsPalLayerSettings
758758
*/
759759
#ifndef SIP_RUN
760760
void calculateLabelSize( const QFontMetricsF *fm, const QString &text, double &labelX, double &labelY, const QgsFeature *f = nullptr, QgsRenderContext *context = nullptr, double *rotatedLabelX SIP_OUT = nullptr, double *rotatedLabelY SIP_OUT = nullptr,
761-
QgsTextDocument *document = nullptr, QgsTextDocumentMetrics *documentMetrics = nullptr );
761+
QgsTextDocument *document = nullptr, QgsTextDocumentMetrics *documentMetrics = nullptr, QRectF *outerBounds = nullptr );
762762
#else
763763
void calculateLabelSize( const QFontMetricsF *fm, const QString &text, double &labelX, double &labelY, const QgsFeature *f = nullptr, QgsRenderContext *context = nullptr, double *rotatedLabelX SIP_OUT = nullptr, double *rotatedLabelY SIP_OUT = nullptr );
764764
#endif

‎src/core/labeling/qgsvectorlayerlabelprovider.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, Q
556556
painter->drawRect( rect );
557557

558558

559-
painter->setPen( QColor( 0, 0, 0, 120 ) );
559+
painter->setPen( QColor( 0, 0, 0, 60 ) );
560560
const QgsMargins &margins = label->getFeaturePart()->feature()->visualMargin();
561561
if ( margins.top() > 0 )
562562
{
@@ -584,7 +584,10 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, Q
584584
outerBoundsPt2.x() - outerBoundsPt1.x(),
585585
outerBoundsPt2.y() - outerBoundsPt1.y() );
586586

587-
painter->setPen( QColor( 255, 0, 255, 140 ) );
587+
QPen pen( QColor( 255, 0, 255, 140 ) );
588+
pen.setCosmetic( true );
589+
pen.setWidth( 1 );
590+
painter->setPen( pen );
588591
painter->drawRect( outerBoundsPixel );
589592
}
590593

@@ -594,13 +597,39 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, Q
594597
const QgsTextDocument &document = textFeature->document();
595598
const int blockCount = document.size();
596599

600+
double prevBlockBaseline = rect.bottom() - rect.top();
601+
597602
// draw block baselines
598-
painter->setPen( QColor( 0, 0, 255, 220 ) );
599603
for ( int blockIndex = 0; blockIndex < blockCount; ++blockIndex )
600604
{
601605
const double blockBaseLine = metrics.baselineOffset( blockIndex, Qgis::TextLayoutMode::Labeling );
602-
painter->drawLine( QPointF( rect.left(), rect.top() + blockBaseLine ),
603-
QPointF( rect.right(), rect.top() + blockBaseLine ) );
606+
607+
const QgsTextBlock &block = document.at( blockIndex );
608+
const int fragmentCount = block.size();
609+
double left = 0;
610+
for ( int fragmentIndex = 0; fragmentIndex < fragmentCount; ++fragmentIndex )
611+
{
612+
const double fragmentVerticalOffset = metrics.fragmentVerticalOffset( blockIndex, fragmentIndex, Qgis::TextLayoutMode::Labeling );
613+
const double right = left + metrics.fragmentHorizontalAdvance( blockIndex, fragmentIndex, Qgis::TextLayoutMode::Labeling );
614+
615+
if ( fragmentIndex > 0 )
616+
{
617+
QPen pen( QColor( 0, 0, 255, 220 ) );
618+
pen.setStyle( Qt::PenStyle::DashLine );
619+
620+
painter->setPen( pen );
621+
622+
painter->drawLine( QPointF( rect.left() + left, rect.top() + blockBaseLine + fragmentVerticalOffset ),
623+
QPointF( rect.left() + left, rect.top() + prevBlockBaseline ) );
624+
625+
}
626+
627+
painter->setPen( QColor( 0, 0, 255, 220 ) );
628+
painter->drawLine( QPointF( rect.left() + left, rect.top() + blockBaseLine + fragmentVerticalOffset ),
629+
QPointF( rect.left() + right, rect.top() + blockBaseLine + fragmentVerticalOffset ) );
630+
left = right;
631+
}
632+
prevBlockBaseline = blockBaseLine;
604633
}
605634
}
606635

0 commit comments

Comments
 (0)
Please sign in to comment.