Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add api for offsetting text from line
  • Loading branch information
nyalldawson committed Mar 31, 2023
1 parent bb66189 commit fbe7c7f
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 4 deletions.
Expand Up @@ -103,6 +103,64 @@ Returns the text format used to render the text.
Sets the text ``format`` used to render the text.

.. seealso:: :py:func:`format`
%End

double offsetFromLine() const;
%Docstring
Returns the offset distance from the line :py:func:`~QgsAnnotationLineTextItem.geometry` to the text's baseline. Units are specified through :py:func:`~QgsAnnotationLineTextItem.offsetFromLineUnit`.

.. seealso:: :py:func:`setOffsetFromLine`

.. seealso:: :py:func:`offsetFromLineUnit`
%End

void setOffsetFromLine( double distance );
%Docstring
Sets the offset ``distance`` from the line :py:func:`~QgsAnnotationLineTextItem.geometry` to the text's baseline. Units are specified through :py:func:`~QgsAnnotationLineTextItem.setOffsetFromLineUnit`.

.. seealso:: :py:func:`offsetFromLine`

.. seealso:: :py:func:`setOffsetFromLineUnit`
%End

void setOffsetFromLineUnit( Qgis::RenderUnit unit );
%Docstring
Sets the ``unit`` for the offset from line :py:func:`~QgsAnnotationLineTextItem.geometry` distance.

.. seealso:: :py:func:`offsetFromLineUnit`

.. seealso:: :py:func:`setOffsetFromLine`
%End

Qgis::RenderUnit offsetFromLineUnit() const;
%Docstring
Returns the units for the offset from line :py:func:`~QgsAnnotationLineTextItem.geometry` distance.

.. seealso:: :py:func:`setOffsetFromLineUnit`

.. seealso:: :py:func:`offsetFromLine`
%End

void setOffsetFromLineMapUnitScale( const QgsMapUnitScale &scale );
%Docstring
Sets the map unit ``scale`` for the offset from line :py:func:`~QgsAnnotationLineTextItem.geometry` distance.

.. seealso:: :py:func:`offsetFromLineMapUnitScale`

.. seealso:: :py:func:`setOffsetFromLineUnit`

.. seealso:: :py:func:`setOffsetFromLine`
%End

const QgsMapUnitScale &offsetFromLineMapUnitScale() const;
%Docstring
Returns the map unit scale for the offset from line :py:func:`~QgsAnnotationLineTextItem.geometry` distance.

.. seealso:: :py:func:`setOffsetFromLineMapUnitScale`

.. seealso:: :py:func:`offsetFromLineUnit`

.. seealso:: :py:func:`offsetFromLine`
%End

private:
Expand Down
27 changes: 25 additions & 2 deletions src/core/annotations/qgsannotationlinetextitem.cpp
Expand Up @@ -22,6 +22,8 @@
#include "qgscurve.h"
#include "qgslinestring.h"
#include "qgstextrenderer.h"
#include "qgsunittypes.h"
#include "qgssymbollayerutils.h"

QgsAnnotationLineTextItem::QgsAnnotationLineTextItem( const QString &text, QgsCurve *curve )
: QgsAnnotationItem()
Expand Down Expand Up @@ -79,13 +81,21 @@ void QgsAnnotationLineTextItem::render( QgsRenderContext &context, QgsFeedback *
}

const QString displayText = QgsExpression::replaceExpressionText( mText, &context.expressionContext(), &context.distanceArea() );
QgsTextRenderer::drawTextOnLine( pts, displayText, context, mTextFormat );

const double offsetFromLine = context.convertToPainterUnits( mOffsetFromLineDistance, mOffsetFromLineUnit, mOffsetFromLineScale );

QgsTextRenderer::drawTextOnLine( pts, displayText, context, mTextFormat, 0, offsetFromLine );
}

bool QgsAnnotationLineTextItem::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
{
element.setAttribute( QStringLiteral( "wkt" ), mCurve->asWkt() );
element.setAttribute( QStringLiteral( "text" ), mText );

element.setAttribute( QStringLiteral( "offsetFromLine" ), qgsDoubleToString( mOffsetFromLineDistance ) );
element.setAttribute( QStringLiteral( "offsetFromLineUnit" ), QgsUnitTypes::encodeUnit( mOffsetFromLineUnit ) );
element.setAttribute( QStringLiteral( "offsetFromLineScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromLineScale ) );

QDomElement textFormatElem = document.createElement( QStringLiteral( "lineTextFormat" ) );
textFormatElem.appendChild( mTextFormat.writeXml( document, context ) );
element.appendChild( textFormatElem );
Expand Down Expand Up @@ -202,6 +212,14 @@ bool QgsAnnotationLineTextItem::readXml( const QDomElement &element, const QgsRe
mTextFormat.readXml( textFormatElem, context );
}

mOffsetFromLineDistance = element.attribute( QStringLiteral( "offsetFromLine" ) ).toDouble();
bool ok = false;
mOffsetFromLineUnit = QgsUnitTypes::decodeRenderUnit( element.attribute( QStringLiteral( "offsetFromLineUnit" ) ), &ok );
if ( !ok )
mOffsetFromLineUnit = Qgis::RenderUnit::Millimeters;

mOffsetFromLineScale = QgsSymbolLayerUtils::decodeMapUnitScale( element.attribute( QStringLiteral( "offsetFromLineScale" ) ) );

readCommonProperties( element, context );

return true;
Expand All @@ -216,20 +234,25 @@ QgsRectangle QgsAnnotationLineTextItem::boundingBox( QgsRenderContext &context )
{
const QString displayText = QgsExpression::replaceExpressionText( mText, &context.expressionContext(), &context.distanceArea() );

const double lineOffsetInMapUnits = context.convertToMapUnits( mOffsetFromLineDistance, mOffsetFromLineUnit, mOffsetFromLineScale );

const double heightInPixels = QgsTextRenderer::textHeight( context, mTextFormat, { displayText} );

// text size has already been calculated using any symbology reference scale factor above -- we need
// to temporarily remove the reference scale here or we'll be undoing the scaling
QgsScopedRenderContextReferenceScaleOverride resetScaleFactor( context, -1.0 );
const double heightInMapUnits = context.convertToMapUnits( heightInPixels, Qgis::RenderUnit::Pixels );

return mCurve->boundingBox().buffered( heightInMapUnits );
return mCurve->boundingBox().buffered( heightInMapUnits + lineOffsetInMapUnits );
}

QgsAnnotationLineTextItem *QgsAnnotationLineTextItem::clone()
{
std::unique_ptr< QgsAnnotationLineTextItem > item = std::make_unique< QgsAnnotationLineTextItem >( mText, mCurve->clone() );
item->setFormat( mTextFormat );
item->setOffsetFromLine( mOffsetFromLineDistance );
item->setOffsetFromLineUnit( mOffsetFromLineUnit );
item->setOffsetFromLineMapUnitScale( mOffsetFromLineScale );
item->copyCommonProperties( this );
return item.release();
}
Expand Down
54 changes: 54 additions & 0 deletions src/core/annotations/qgsannotationlinetextitem.h
Expand Up @@ -106,12 +106,66 @@ class CORE_EXPORT QgsAnnotationLineTextItem : public QgsAnnotationItem
*/
void setFormat( const QgsTextFormat &format );

/**
* Returns the offset distance from the line geometry() to the text's baseline. Units are specified through offsetFromLineUnit().
*
* \see setOffsetFromLine()
* \see offsetFromLineUnit()
*/
double offsetFromLine() const { return mOffsetFromLineDistance; }

/**
* Sets the offset \a distance from the line geometry() to the text's baseline. Units are specified through setOffsetFromLineUnit().
*
* \see offsetFromLine()
* \see setOffsetFromLineUnit()
*/
void setOffsetFromLine( double distance ) { mOffsetFromLineDistance = distance; }

/**
* Sets the \a unit for the offset from line geometry() distance.
*
* \see offsetFromLineUnit()
* \see setOffsetFromLine()
*/
void setOffsetFromLineUnit( Qgis::RenderUnit unit ) { mOffsetFromLineUnit = unit; }

/**
* Returns the units for the offset from line geometry() distance.
*
* \see setOffsetFromLineUnit()
* \see offsetFromLine()
*/
Qgis::RenderUnit offsetFromLineUnit() const { return mOffsetFromLineUnit; }

/**
* Sets the map unit \a scale for the offset from line geometry() distance.
*
* \see offsetFromLineMapUnitScale()
* \see setOffsetFromLineUnit()
* \see setOffsetFromLine()
*/
void setOffsetFromLineMapUnitScale( const QgsMapUnitScale &scale ) { mOffsetFromLineScale = scale; }

/**
* Returns the map unit scale for the offset from line geometry() distance.
*
* \see setOffsetFromLineMapUnitScale()
* \see offsetFromLineUnit()
* \see offsetFromLine()
*/
const QgsMapUnitScale &offsetFromLineMapUnitScale() const { return mOffsetFromLineScale; }

private:

QString mText;
std::unique_ptr< QgsCurve > mCurve;
QgsTextFormat mTextFormat;

double mOffsetFromLineDistance = 0;
Qgis::RenderUnit mOffsetFromLineUnit = Qgis::RenderUnit::Millimeters;
QgsMapUnitScale mOffsetFromLineScale;

#ifdef SIP_RUN
QgsAnnotationLineTextItem( const QgsAnnotationLineTextItem &other );
#endif
Expand Down
22 changes: 20 additions & 2 deletions src/core/textrenderer/qgstextrenderer.cpp
Expand Up @@ -28,6 +28,7 @@
#include "qgsunittypes.h"
#include "qgstextmetrics.h"
#include "qgstextrendererutils.h"
#include "qgsgeos.h"

#include <optional>

Expand Down Expand Up @@ -171,8 +172,25 @@ void QgsTextRenderer::drawDocumentOnLine( const QPolygonF &line, const QgsTextFo
QPolygonF labelBaselineCurve = line;
if ( !qgsDoubleNear( offsetFromLine, 0 ) )
{
QgsGeometry curve = QgsGeometry::fromQPolygonF( line );
labelBaselineCurve = curve.offsetCurve( offsetFromLine, 4, Qgis::JoinStyle::Round, 2 ).asQPolygonF();
std::unique_ptr < QgsLineString > ring( QgsLineString::fromQPolygonF( line ) );
QgsGeos geos( ring.get() );
std::unique_ptr < QgsLineString > offsetCurve( dynamic_cast< QgsLineString * >( geos.offsetCurve( offsetFromLine, 4, Qgis::JoinStyle::Round, 2 ) ) );
if ( !offsetCurve )
return;

#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
if ( offsetFromLine < 0 )
{
// geos < 3.11 reverses the direction of offset curves with negative distances -- we don't want that!
std::unique_ptr < QgsLineString > reversed( offsetCurve->reversed() );
if ( !reversed )
return;

offsetCurve = std::move( reversed );
}
#endif

labelBaselineCurve = offsetCurve->asQPolygonF();
}

const double fontScale = calculateScaleFactorForFormat( context, format );
Expand Down

0 comments on commit fbe7c7f

Please sign in to comment.