Skip to content

Commit

Permalink
[labeling] Fix setting a wrap character conflicts with newlines
Browse files Browse the repository at this point in the history
in label text (fix #12750)
  • Loading branch information
nyalldawson committed May 18, 2015
1 parent 9d7a650 commit e0aa69d
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 8 deletions.
9 changes: 9 additions & 0 deletions python/core/qgspallabeling.sip
Expand Up @@ -741,6 +741,15 @@ class QgsPalLabeling : QgsLabelingEngineInterface
//! @deprecated since 2.4 - settings are always stored in project
void setStoredWithProject( bool store ) /Deprecated/;

/** 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
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 @@ -3410,6 +3410,25 @@ void QgsPalLabeling::registerFeature( const QString& layerID, QgsFeature& f, con
lyr.registerFeature( f, context, dxfLayer );
}

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;
}

void QgsPalLabeling::registerDiagramFeature( const QString& layerID, QgsFeature& feat, const QgsRenderContext& context )
{
//get diagram layer settings, diagram renderer
Expand Down Expand Up @@ -4309,8 +4328,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 @@ -4341,12 +4358,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 @@ -4360,8 +4377,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
9 changes: 9 additions & 0 deletions src/core/qgspallabeling.h
Expand Up @@ -815,6 +815,15 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
//! @deprecated since 2.4 - settings are always stored in project
Q_DECL_DEPRECATED void setStoredWithProject( bool store ) { Q_UNUSED( store ); }

/** 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 @@ -149,3 +149,5 @@ ADD_QGIS_TEST(maplayerstylemanager testqgsmaplayerstylemanager.cpp )
ADD_QGIS_TEST(pointlocatortest testqgspointlocator.cpp )
ADD_QGIS_TEST(snappingutilstest testqgssnappingutils.cpp )
ADD_QGIS_TEST(imageoperationtest testqgsimageoperation.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 e0aa69d

Please sign in to comment.