From 4b4075b5b94d84abf51831409626b6321898c40e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 19 May 2015 06:16:32 +1000 Subject: [PATCH] [labeling] Fix setting a wrap character conflicts with newlines in label text (fix #12750) --- python/core/qgspallabeling.sip | 8 ++++ src/core/qgspallabeling.cpp | 32 +++++++++---- src/core/qgspallabeling.h | 10 ++++ tests/src/core/CMakeLists.txt | 2 + tests/src/core/testqgspallabeling.cpp | 67 +++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 tests/src/core/testqgspallabeling.cpp diff --git a/python/core/qgspallabeling.sip b/python/core/qgspallabeling.sip index 14e539e2344..d74feb9ae88 100644 --- a/python/core/qgspallabeling.sip +++ b/python/core/qgspallabeling.sip @@ -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 diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index 6839bf854ae..179de58f1aa 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -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 @@ -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 ) @@ -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 ) @@ -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 ) @@ -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; diff --git a/src/core/qgspallabeling.h b/src/core/qgspallabeling.h index 7af566ab59c..51df15c7bf9 100644 --- a/src/core/qgspallabeling.h +++ b/src/core/qgspallabeling.h @@ -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 ); @@ -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, diff --git a/tests/src/core/CMakeLists.txt b/tests/src/core/CMakeLists.txt index 902074905fe..052daa51b5c 100644 --- a/tests/src/core/CMakeLists.txt +++ b/tests/src/core/CMakeLists.txt @@ -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) + diff --git a/tests/src/core/testqgspallabeling.cpp b/tests/src/core/testqgspallabeling.cpp new file mode 100644 index 00000000000..93b88ba0c7a --- /dev/null +++ b/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 +#include +#include +#include +#include + +#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"