Skip to content

Commit

Permalink
[FEATURE][layouts] Use QgsTextRenderer for drawing map grid text in l…
Browse files Browse the repository at this point in the history
…ayouts

Allows for grid annotations which use buffers, shadows, background shapes, etc!
  • Loading branch information
nyalldawson committed Jul 8, 2020
1 parent 573e46b commit ff14c6f
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 241 deletions.
38 changes: 34 additions & 4 deletions python/core/auto_generated/layout/qgslayoutitemmapgrid.sip.in
Expand Up @@ -565,32 +565,62 @@ Returns whether annotations are shown for the grid.
.. seealso:: :py:func:`setAnnotationEnabled`
%End

void setAnnotationFont( const QFont &font );
void setAnnotationTextFormat( const QgsTextFormat &format );
%Docstring
Sets the text ``format`` to use when rendering grid annotations.

.. seealso:: :py:func:`annotationTextFormat`

.. versionadded:: 3.16
%End

QgsTextFormat annotationTextFormat() const;
%Docstring
Returns the text format used when rendering grid annotations.

.. seealso:: :py:func:`setAnnotationTextFormat`

.. versionadded:: 3.16
%End

void setAnnotationFont( const QFont &font ) /Deprecated/;
%Docstring
Sets the ``font`` used for drawing grid annotations.

.. seealso:: :py:func:`annotationFont`

.. deprecated::
use setAnnotationTextFormat() instead
%End

QFont annotationFont() const;
QFont annotationFont() const /Deprecated/;
%Docstring
Returns the font used for drawing grid annotations.

.. seealso:: :py:func:`setAnnotationFont`

.. deprecated::
use annotationTextFormat() instead
%End

void setAnnotationFontColor( const QColor &color );
void setAnnotationFontColor( const QColor &color ) /Deprecated/;
%Docstring
Sets the font ``color`` used for drawing grid annotations.

.. seealso:: :py:func:`annotationFontColor`

.. deprecated::
use setAnnotationTextFormat() instead
%End

QColor annotationFontColor() const;
QColor annotationFontColor() const /Deprecated/;
%Docstring
Returns the font color used for drawing grid annotations.

.. seealso:: :py:func:`setAnnotationFontColor`

.. deprecated::
use annotationTextFormat() instead
%End

void setAnnotationPrecision( const int precision );
Expand Down
17 changes: 15 additions & 2 deletions src/core/layout/qgscompositionconverter.cpp
Expand Up @@ -984,8 +984,21 @@ bool QgsCompositionConverter::readMapXml( QgsLayoutItemMap *layoutItem, const QD
mapGrid->setAnnotationFrameDistance( annotationElem.attribute( QStringLiteral( "frameDistance" ), QStringLiteral( "0" ) ).toDouble() );
QFont annotationFont;
annotationFont.fromString( annotationElem.attribute( QStringLiteral( "font" ), QString() ) );
mapGrid->setAnnotationFont( annotationFont );
mapGrid->setAnnotationFontColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );

QgsTextFormat annotationFormat = mapGrid->annotationTextFormat();
annotationFormat.setFont( annotationFont );
if ( annotationFont.pointSizeF() > 0 )
{
annotationFormat.setSize( annotationFont.pointSizeF() );
annotationFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
}
else if ( annotationFont.pixelSize() > 0 )
{
annotationFormat.setSize( annotationFont.pixelSize() );
annotationFormat.setSizeUnit( QgsUnitTypes::RenderPixels );
}
annotationFormat.setColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
mapGrid->setAnnotationTextFormat( annotationFormat );

mapGrid->setAnnotationPrecision( annotationElem.attribute( QStringLiteral( "precision" ), QStringLiteral( "3" ) ).toInt() );
}
Expand Down
97 changes: 73 additions & 24 deletions src/core/layout/qgslayoutitemmapgrid.cpp
Expand Up @@ -35,6 +35,7 @@
#include "qgssettings.h"
#include "qgscoordinateformatter.h"
#include "qgsstyleentityvisitor.h"
#include "qgstextrenderer.h"

#include <QPainter>
#include <QPen>
Expand Down Expand Up @@ -166,7 +167,9 @@ QgsLayoutItemMapGrid::QgsLayoutItemMapGrid( const QString &name, QgsLayoutItemMa
QString defaultFontString = settings.value( QStringLiteral( "LayoutDesigner/defaultFont" ), QVariant(), QgsSettings::Gui ).toString();
if ( !defaultFontString.isEmpty() )
{
mGridAnnotationFont.setFamily( defaultFontString );
QFont font;
font.setFamily( defaultFontString );
mAnnotationFormat.setFont( font );
}

createDefaultGridLineSymbol();
Expand Down Expand Up @@ -268,8 +271,7 @@ bool QgsLayoutItemMapGrid::writeXml( QDomElement &elem, QDomDocument &doc, const
mapGridElem.setAttribute( QStringLiteral( "topAnnotationDirection" ), mTopGridAnnotationDirection );
mapGridElem.setAttribute( QStringLiteral( "bottomAnnotationDirection" ), mBottomGridAnnotationDirection );
mapGridElem.setAttribute( QStringLiteral( "frameAnnotationDistance" ), QString::number( mAnnotationFrameDistance ) );
mapGridElem.appendChild( QgsFontUtils::toXmlElement( mGridAnnotationFont, doc, QStringLiteral( "annotationFontProperties" ) ) );
mapGridElem.setAttribute( QStringLiteral( "annotationFontColor" ), QgsSymbolLayerUtils::encodeColor( mGridAnnotationFontColor ) );
mapGridElem.appendChild( mAnnotationFormat.writeXml( doc, context ) );
mapGridElem.setAttribute( QStringLiteral( "annotationPrecision" ), mGridAnnotationPrecision );
mapGridElem.setAttribute( QStringLiteral( "unit" ), mGridUnit );
mapGridElem.setAttribute( QStringLiteral( "blendMode" ), mBlendMode );
Expand Down Expand Up @@ -364,11 +366,24 @@ bool QgsLayoutItemMapGrid::readXml( const QDomElement &itemElem, const QDomDocum
mTopGridAnnotationDirection = QgsLayoutItemMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "topAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
mBottomGridAnnotationDirection = QgsLayoutItemMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "bottomAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
mAnnotationFrameDistance = itemElem.attribute( QStringLiteral( "frameAnnotationDistance" ), QStringLiteral( "0" ) ).toDouble();
if ( !QgsFontUtils::setFromXmlChildNode( mGridAnnotationFont, itemElem, QStringLiteral( "annotationFontProperties" ) ) )

if ( !itemElem.firstChildElement( "text-style" ).isNull() )
{
mAnnotationFormat.readXml( itemElem, context );
}
else
{
mGridAnnotationFont.fromString( itemElem.attribute( QStringLiteral( "annotationFont" ), QString() ) );
QFont font;
if ( !QgsFontUtils::setFromXmlChildNode( font, itemElem, "annotationFontProperties" ) )
{
font.fromString( itemElem.attribute( "annotationFont", QString() ) );
}
mAnnotationFormat.setFont( font );
mAnnotationFormat.setSize( font.pointSizeF() );
mAnnotationFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
mAnnotationFormat.setColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( "annotationFontColor", "0,0,0,255" ) ) );
}
mGridAnnotationFontColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "annotationFontColor" ), QStringLiteral( "0,0,0,255" ) ) );

mGridAnnotationPrecision = itemElem.attribute( QStringLiteral( "annotationPrecision" ), QStringLiteral( "3" ) ).toInt();
int gridUnitInt = itemElem.attribute( QStringLiteral( "unit" ), QString::number( MapUnit ) ).toInt();
mGridUnit = ( gridUnitInt <= static_cast< int >( DynamicPageSizeBased ) ) ? static_cast< GridUnit >( gridUnitInt ) : MapUnit;
Expand Down Expand Up @@ -585,6 +600,7 @@ void QgsLayoutItemMapGrid::draw( QPainter *p )
//setup render context
QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( mLayout, p );
context.setForceVectorOutput( true );
context.setFlag( QgsRenderContext::ApplyScalingWorkaroundForTextRendering, true );
QgsExpressionContext expressionContext = createExpressionContext();
context.setExpressionContext( expressionContext );

Expand Down Expand Up @@ -624,7 +640,7 @@ void QgsLayoutItemMapGrid::draw( QPainter *p )

if ( mShowGridAnnotation )
{
drawCoordinateAnnotations( p, horizontalLines, verticalLines, context.expressionContext() );
drawCoordinateAnnotations( context, horizontalLines, verticalLines, context.expressionContext() );
}
}

Expand Down Expand Up @@ -1045,39 +1061,40 @@ void QgsLayoutItemMapGrid::drawGridFrameLineBorder( QPainter *p, QgsLayoutItemMa
}
}

void QgsLayoutItemMapGrid::drawCoordinateAnnotations( QPainter *p, const QList< QPair< double, QLineF > > &hLines, const QList< QPair< double, QLineF > > &vLines, QgsExpressionContext &expressionContext,
void QgsLayoutItemMapGrid::drawCoordinateAnnotations( QgsRenderContext &context, const QList< QPair< double, QLineF > > &hLines, const QList< QPair< double, QLineF > > &vLines, QgsExpressionContext &expressionContext,
GridExtension *extension ) const
{
QString currentAnnotationString;
QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
for ( ; it != hLines.constEnd(); ++it )
{
currentAnnotationString = gridAnnotationString( it->first, QgsLayoutItemMapGrid::Latitude, expressionContext );
drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString, QgsLayoutItemMapGrid::Latitude, extension );
drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString, QgsLayoutItemMapGrid::Latitude, extension );
drawCoordinateAnnotation( context, it->second.p1(), currentAnnotationString, QgsLayoutItemMapGrid::Latitude, extension );
drawCoordinateAnnotation( context, it->second.p2(), currentAnnotationString, QgsLayoutItemMapGrid::Latitude, extension );
}

it = vLines.constBegin();
for ( ; it != vLines.constEnd(); ++it )
{
currentAnnotationString = gridAnnotationString( it->first, QgsLayoutItemMapGrid::Longitude, expressionContext );
drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString, QgsLayoutItemMapGrid::Longitude, extension );
drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString, QgsLayoutItemMapGrid::Longitude, extension );
drawCoordinateAnnotation( context, it->second.p1(), currentAnnotationString, QgsLayoutItemMapGrid::Longitude, extension );
drawCoordinateAnnotation( context, it->second.p2(), currentAnnotationString, QgsLayoutItemMapGrid::Longitude, extension );
}
}

void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, const QString &annotationString, const AnnotationCoordinate coordinateType, GridExtension *extension ) const
void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QgsRenderContext &context, QPointF pos, const QString &annotationString, const AnnotationCoordinate coordinateType, GridExtension *extension ) const
{
if ( !mMap )
{
return;
}

QgsLayoutItemMapGrid::BorderSide frameBorder = borderForLineCoord( pos, coordinateType );
double textWidth = QgsLayoutUtils::textWidthMM( mGridAnnotationFont, annotationString );
double textWidth = QgsTextRenderer::textWidth( context, mAnnotationFormat, QStringList() << annotationString ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
//relevant for annotations is the height of digits
double textHeight = extension ? QgsLayoutUtils::fontAscentMM( mGridAnnotationFont )
: QgsLayoutUtils::fontHeightCharacterMM( mGridAnnotationFont, QChar( '0' ) );
const QFontMetricsF metrics = QgsTextRenderer::fontMetrics( context, mAnnotationFormat, QgsTextRenderer::FONT_WORKAROUND_SCALE );
double textHeight = ( extension ? ( QgsTextRenderer::textHeight( context, mAnnotationFormat, QChar(), true ) )
: ( QgsTextRenderer::textHeight( context, mAnnotationFormat, '0', false ) ) ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
double xpos = pos.x();
double ypos = pos.y();
int rotation = 0;
Expand Down Expand Up @@ -1398,23 +1415,25 @@ void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, c
}
}

if ( extension || !p )
if ( extension || !context.painter() )
return;

drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
drawAnnotation( context, QPointF( xpos, ypos ), rotation, annotationString );
}

void QgsLayoutItemMapGrid::drawAnnotation( QPainter *p, QPointF pos, int rotation, const QString &annotationText ) const
void QgsLayoutItemMapGrid::drawAnnotation( QgsRenderContext &context, QPointF pos, int rotation, const QString &annotationText ) const
{
if ( !mMap )
{
return;
}

QgsScopedQPainterState painterState( p );
p->translate( pos );
p->rotate( rotation );
QgsLayoutUtils::drawText( p, QPointF( 0, 0 ), annotationText, mGridAnnotationFont, mGridAnnotationFontColor );
QgsScopedQPainterState painterState( context.painter() );
context.painter()->translate( pos );
context.painter()->rotate( rotation );

QgsScopedRenderContextScaleToPixels scale( context );
QgsTextRenderer::drawText( QPointF( 0, 0 ), 0, QgsTextRenderer::AlignLeft, QStringList() << annotationText, context, mAnnotationFormat );
}

QString QgsLayoutItemMapGrid::gridAnnotationString( double value, QgsLayoutItemMapGrid::AnnotationCoordinate coord, QgsExpressionContext &expressionContext ) const
Expand Down Expand Up @@ -2122,6 +2141,36 @@ QgsMarkerSymbol *QgsLayoutItemMapGrid::markerSymbol()
return mGridMarkerSymbol.get();
}

void QgsLayoutItemMapGrid::setAnnotationFont( const QFont &font )
{
mAnnotationFormat.setFont( font );
if ( font.pointSizeF() > 0 )
{
mAnnotationFormat.setSize( font.pointSizeF() );
mAnnotationFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
}
else if ( font.pixelSize() > 0 )
{
mAnnotationFormat.setSize( font.pixelSize() );
mAnnotationFormat.setSizeUnit( QgsUnitTypes::RenderPixels );
}
}

QFont QgsLayoutItemMapGrid::annotationFont() const
{
return mAnnotationFormat.toQFont();
}

void QgsLayoutItemMapGrid::setAnnotationFontColor( const QColor &color )
{
mAnnotationFormat.setColor( color );
}

QColor QgsLayoutItemMapGrid::annotationFontColor() const
{
return mAnnotationFormat.color();
}

void QgsLayoutItemMapGrid::setAnnotationDisplay( const QgsLayoutItemMapGrid::DisplayMode display, const QgsLayoutItemMapGrid::BorderSide border )
{
switch ( border )
Expand Down Expand Up @@ -2220,7 +2269,7 @@ void QgsLayoutItemMapGrid::calculateMaxExtension( double &top, double &right, do

if ( mShowGridAnnotation )
{
drawCoordinateAnnotations( nullptr, horizontalLines, verticalLines, context.expressionContext(), &extension );
drawCoordinateAnnotations( context, horizontalLines, verticalLines, context.expressionContext(), &extension );
}

top = extension.top;
Expand Down

0 comments on commit ff14c6f

Please sign in to comment.