Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[labeling] Fix data-defined tab distance property #60211

Merged
merged 2 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion src/core/labeling/qgspallabeling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2104,6 +2104,34 @@ std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerFeatureWithDetails
parseDropShadow( context );
}

if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::TabStopDistance ) )
{
QList<QgsTextFormat::Tab> tabPositions;
if ( dataDefinedValues.value( QgsPalLayerSettings::Property::TabStopDistance ).userType() == QMetaType::Type::QVariantList )
{
const QVariantList parts = dataDefinedValues.value( QgsPalLayerSettings::Property::TabStopDistance ).toList();
for ( const QVariant &part : parts )
{
tabPositions.append( QgsTextFormat::Tab( part.toDouble() ) );
}
evaluatedFormat.setTabPositions( tabPositions );
}
else if ( dataDefinedValues.value( QgsPalLayerSettings::Property::TabStopDistance ).userType() == QMetaType::Type::QStringList )
{
const QStringList parts = dataDefinedValues.value( QgsPalLayerSettings::Property::TabStopDistance ).toStringList();
for ( const QString &part : parts )
{
tabPositions.append( QgsTextFormat::Tab( part.toDouble() ) );
}
evaluatedFormat.setTabPositions( tabPositions );
}
else
{
evaluatedFormat.setTabPositions( tabPositions );
evaluatedFormat.setTabStopDistance( dataDefinedValues.value( QgsPalLayerSettings::Property::TabStopDistance ).toDouble() );
}
}

evaluatedFormat.setFont( labelFont );
// undo scaling by symbology reference scale, as this would have been applied in the previous call to QgsTextRenderer::sizeToPixel and we risk
// double-applying it if we don't re-adjust, since all the text format metric calculations assume an unscaled format font size is present
Expand Down Expand Up @@ -2150,6 +2178,7 @@ std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerFeatureWithDetails
{
capitalization = static_cast< Qgis::Capitalization >( mFormat.font().capitalization() );
}

// data defined font capitalization?
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontCase ) )
{
Expand Down Expand Up @@ -3684,6 +3713,15 @@ void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
evalAutoWrapLength = exprVal.toInt();
}

if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::TabStopDistance ) )
{
exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Property::TabStopDistance, context.expressionContext() );
if ( !QgsVariantUtils::isNull( exprVal ) )
{
dataDefinedValues.insert( QgsPalLayerSettings::Property::TabStopDistance, exprVal );
}
}

// data defined multiline height?
dataDefinedValEval( DDDouble, QgsPalLayerSettings::Property::MultiLineHeight, exprVal, context.expressionContext() );

Expand Down Expand Up @@ -4411,6 +4449,36 @@ void QgsPalLabeling::dataDefinedTextFormatting( QgsPalLayerSettings &tmpLyr,
tmpLyr.setFormat( format );
}

if ( ddValues.contains( QgsPalLayerSettings::Property::TabStopDistance ) )
{
QgsTextFormat format = tmpLyr.format();
QList<QgsTextFormat::Tab> tabPositions;
if ( ddValues.value( QgsPalLayerSettings::Property::TabStopDistance ).userType() == QMetaType::Type::QVariantList )
{
const QVariantList parts = ddValues.value( QgsPalLayerSettings::Property::TabStopDistance ).toList();
for ( const QVariant &part : parts )
{
tabPositions.append( QgsTextFormat::Tab( part.toDouble() ) );
}
format.setTabPositions( tabPositions );
}
else if ( ddValues.value( QgsPalLayerSettings::Property::TabStopDistance ).userType() == QMetaType::Type::QStringList )
{
const QStringList parts = ddValues.value( QgsPalLayerSettings::Property::TabStopDistance ).toStringList();
for ( const QString &part : parts )
{
tabPositions.append( QgsTextFormat::Tab( part.toDouble() ) );
}
format.setTabPositions( tabPositions );
}
else
{
format.setTabPositions( tabPositions );
format.setTabStopDistance( ddValues.value( QgsPalLayerSettings::Property::TabStopDistance ).toDouble() );
}
tmpLyr.setFormat( format );
}

if ( ddValues.contains( QgsPalLayerSettings::Property::MultiLineAlignment ) )
{
tmpLyr.multilineAlign = static_cast< Qgis::LabelMultiLineAlignment >( ddValues.value( QgsPalLayerSettings::Property::MultiLineAlignment ).toInt() );
Expand Down Expand Up @@ -4449,7 +4517,6 @@ void QgsPalLabeling::dataDefinedTextFormatting( QgsPalLayerSettings &tmpLyr,
{
tmpLyr.lineSettings().setReverseDirectionSymbol( ddValues.value( QgsPalLayerSettings::Property::DirSymbReverse ).toBool() );
}

}
}

Expand Down
59 changes: 59 additions & 0 deletions tests/src/core/testqgslabelingengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class TestQgsLabelingEngine : public QgsTest
void parallelOverrun();
void testDataDefinedLabelAllParts();
void testDataDefinedPlacementPositionPoint();
void testDataDefinedLabelTabs();
void testVerticalOrientation();
void testVerticalOrientationLetterLineSpacing();
void testRotationBasedOrientationPoint();
Expand Down Expand Up @@ -5185,6 +5186,64 @@ void TestQgsLabelingEngine::testDataDefinedPlacementPositionPoint()
vl->setLabeling( nullptr );
}

void TestQgsLabelingEngine::testDataDefinedLabelTabs()
{
// test curved label rendering with tab characters
QgsPalLayerSettings settings;
setDefaultLabelParams( settings );

QgsTextFormat format = settings.format();
format.setSize( 30 );
format.setColor( QColor( 0, 0, 0 ) );
format.setTabStopDistance( 28 );
format.setTabStopDistanceUnit( Qgis::RenderUnit::Millimeters );
settings.setFormat( format );
settings.dataDefinedProperties().setProperty( QgsPalLayerSettings::Property::TabStopDistance, QgsProperty::fromExpression( QStringLiteral( "48" ) ) );

settings.fieldName = QStringLiteral( "'test of\ttab\ttext'" );
settings.isExpression = true;
settings.placement = Qgis::LabelPlacement::Horizontal;
settings.labelPerPart = false;
settings.lineSettings().setLineAnchorPercent( 0.5 );
settings.lineSettings().setAnchorType( QgsLabelLineSettings::AnchorType::Strict );
settings.lineSettings().setPlacementFlags( Qgis::LabelLinePlacementFlag::AboveLine | Qgis::LabelLinePlacementFlag::MapOrientation );
settings.lineSettings().setAnchorTextPoint( QgsLabelLineSettings::AnchorTextPoint::CenterOfText );

std::unique_ptr<QgsVectorLayer> vl2( new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:3946&field=id:integer" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
vl2->setRenderer( new QgsNullSymbolRenderer() );

QgsFeature f;
f.setAttributes( QgsAttributes() << 1 );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (190000 5000010, 190100 5000000, 190200 5000000)" ) ) );
QVERIFY( vl2->dataProvider()->addFeature( f ) );

vl2->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
vl2->setLabelsEnabled( true );

// make a fake render context
const QSize size( 640, 480 );
QgsMapSettings mapSettings;
mapSettings.setLabelingEngineSettings( createLabelEngineSettings() );
mapSettings.setDestinationCrs( vl2->crs() );

mapSettings.setOutputSize( size );
mapSettings.setExtent( f.geometry().boundingBox() );
mapSettings.setLayers( QList<QgsMapLayer *>() << vl2.get() );
mapSettings.setOutputDpi( 96 );

QgsLabelingEngineSettings engineSettings = mapSettings.labelingEngineSettings();
engineSettings.setFlag( Qgis::LabelingFlag::UsePartialCandidates, false );
engineSettings.setFlag( Qgis::LabelingFlag::DrawCandidates, true );
mapSettings.setLabelingEngineSettings( engineSettings );

QgsMapRendererSequentialJob job( mapSettings );
job.start();
job.waitForFinished();

QImage img = job.renderedImage();
QGSVERIFYIMAGECHECK( "data_defined_tabs", "data_defined_tabs", img, QString(), 20, QSize( 0, 0 ), 2 );
}

void TestQgsLabelingEngine::testVerticalOrientation()
{
const QSize size( 640, 480 );
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading