Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[labeling] Fix setting a wrap character conflicts with newlines
in label text (fix #12750)
  • Loading branch information
nyalldawson committed May 18, 2015
1 parent a2a2781 commit 4b4075b
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 8 deletions.
8 changes: 8 additions & 0 deletions python/core/qgspallabeling.sip
Expand Up @@ -764,6 +764,14 @@ class QgsPalLabeling : QgsLabelingEngineInterface
*/
static bool geometryRequiresPreparation( const QgsGeometry *geometry, const QgsRenderContext &context, const QgsCoordinateTransform *ct, QgsGeometry *clipGeometry = 0 );

/** Splits a text string to a list of seperate lines, using a specified wrap character.
* The text string will be split on either newline characters or the wrap character.
* @param text text string to split
* @param wrapCharacter additional character to wrap on
* @returns list of text split to lines
* @note added in QGIS 2.9
*/
static QStringList splitToLines( const QString& text, const QString& wrapCharacter );

protected:
// update temporary QgsPalLayerSettings with any data defined text style values
Expand Down
32 changes: 24 additions & 8 deletions src/core/qgspallabeling.cpp
Expand Up @@ -1405,12 +1405,12 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString t
}
else
{
text.prepend( dirSym + wrapchr ); // SymbolAbove or SymbolBelow
text.prepend( dirSym + QString( "\n" ) ); // SymbolAbove or SymbolBelow
}
}

double w = 0.0, h = 0.0;
QStringList multiLineSplit = text.split( wrapchr );
QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, wrapchr );
int lines = multiLineSplit.size();

double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
Expand Down Expand Up @@ -3393,6 +3393,25 @@ bool QgsPalLabeling::geometryRequiresPreparation( const QgsGeometry* geometry, c
return false;
}

QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wrapCharacter )
{
QStringList multiLineSplit;
if ( !wrapCharacter.isEmpty() && wrapCharacter != QString( "\n" ) )
{
//wrap on both the wrapchr and new line characters
foreach ( QString line, text.split( wrapCharacter ) )
{
multiLineSplit.append( line.split( QString( "\n" ) ) );
}
}
else
{
multiLineSplit = text.split( "\n" );
}

return multiLineSplit;
}

QgsGeometry* QgsPalLabeling::prepareGeometry( const QgsGeometry* geometry, const QgsRenderContext& context, const QgsCoordinateTransform* ct, double minSize, QgsGeometry* clipGeometry )
{
if ( !geometry )
Expand Down Expand Up @@ -4436,8 +4455,6 @@ void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QgsRenderContext& con
QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) );
QFontMetricsF* labelfm = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->getLabelFontMetrics();

QString wrapchr = !tmpLyr.wrapChar.isEmpty() ? tmpLyr.wrapChar : QString( "\n" );

//add the direction symbol if needed
if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line &&
tmpLyr.addDirectionSymbol )
Expand Down Expand Up @@ -4468,12 +4485,12 @@ void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QgsRenderContext& con
if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolAbove )
{
prependSymb = true;
symb = symb + wrapchr;
symb = symb + QString( "\n" );
}
else if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolBelow )
{
prependSymb = false;
symb = wrapchr + symb;
symb = QString( "\n" ) + symb;
}

if ( prependSymb )
Expand All @@ -4487,8 +4504,7 @@ void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QgsRenderContext& con
}

//QgsDebugMsgLevel( "drawLabel " + txt, 4 );

QStringList multiLineList = txt.split( wrapchr );
QStringList multiLineList = QgsPalLabeling::splitToLines( text, tmpLyr.wrapChar );
int lines = multiLineList.size();

double labelWidest = 0.0;
Expand Down
10 changes: 10 additions & 0 deletions src/core/qgspallabeling.h
Expand Up @@ -527,6 +527,7 @@ class CORE_EXPORT QgsPalLayerSettings
bool showingShadowRects; // whether to show debug rectangles for drop shadows

private:

void readDataDefinedPropertyMap( QgsVectorLayer* layer,
QMap < QgsPalLayerSettings::DataDefinedProperties,
QgsDataDefined* > & propertyMap );
Expand Down Expand Up @@ -833,6 +834,15 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
*/
static bool geometryRequiresPreparation( const QgsGeometry *geometry, const QgsRenderContext &context, const QgsCoordinateTransform *ct, QgsGeometry *clipGeometry = 0 );

/** Splits a text string to a list of seperate lines, using a specified wrap character.
* The text string will be split on either newline characters or the wrap character.
* @param text text string to split
* @param wrapCharacter additional character to wrap on
* @returns list of text split to lines
* @note added in QGIS 2.9
*/
static QStringList splitToLines( const QString& text, const QString& wrapCharacter );

protected:
// update temporary QgsPalLayerSettings with any data defined text style values
void dataDefinedTextStyle( QgsPalLayerSettings& tmpLyr,
Expand Down
2 changes: 2 additions & 0 deletions tests/src/core/CMakeLists.txt
Expand Up @@ -157,4 +157,6 @@ ADD_QGIS_TEST(painteffecttest testqgspainteffect.cpp)
ADD_QGIS_TEST(painteffectregistrytest testqgspainteffectregistry.cpp)
ADD_QGIS_TEST(statisticalsummarytest testqgsstatisticalsummary.cpp)
ADD_QGIS_TEST(histogramtest testqgshistogram.cpp)
ADD_QGIS_TEST(pallabelingtest testqgspallabeling.cpp)


67 changes: 67 additions & 0 deletions tests/src/core/testqgspallabeling.cpp
@@ -0,0 +1,67 @@
/***************************************************************************
testqgspallabeling.cpp
----------------------
Date : May 2015
Copyright : (C) 2015 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QtTest/QtTest>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QSharedPointer>

#include "qgspallabeling.h"

class TestQgsPalLabeling: public QObject
{
Q_OBJECT

private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init();// will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.
void wrapChar();//test wrapping text lines

private:
};

void TestQgsPalLabeling::initTestCase()
{
}

void TestQgsPalLabeling::cleanupTestCase()
{

}

void TestQgsPalLabeling::init()
{

}

void TestQgsPalLabeling::cleanup()
{

}

void TestQgsPalLabeling::wrapChar()
{
QCOMPARE( QgsPalLabeling::splitToLines( "nolines", QString() ) , QStringList() << "nolines" );
QCOMPARE( QgsPalLabeling::splitToLines( "new line\nonly", QString() ), QStringList() << "new line" << "only" );
QCOMPARE( QgsPalLabeling::splitToLines( "new line\nonly", QString( "\n" ) ), QStringList() << "new line" << "only" );
QCOMPARE( QgsPalLabeling::splitToLines( "mixed new line\nand char", QString( " " ) ), QStringList() << "mixed" << "new" << "line" << "and" << "char" );
QCOMPARE( QgsPalLabeling::splitToLines( "no matching chars", QString( "#" ) ), QStringList() << "no matching chars" );
QCOMPARE( QgsPalLabeling::splitToLines( "no\nmatching\nchars", QString( "#" ) ), QStringList() << "no" << "matching" << "chars" );
}

QTEST_MAIN( TestQgsPalLabeling )
#include "testqgspallabeling.moc"

0 comments on commit 4b4075b

Please sign in to comment.