Skip to content

Commit 9b71d08

Browse files
committed
Merge pull request #1208 from nirvn/expression_wordwrap3
[expression] implement a wordwrap function (fix #9412)
2 parents 86ea4bf + a7cb2b2 commit 9b71d08

File tree

3 files changed

+89
-1
lines changed

3 files changed

+89
-1
lines changed

resources/function_help/wordwrap

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<h3>wordwrap() function</h3>
2+
Returns a string wrapped to a maximum/minimum number of characters.
3+
4+
<p><h4>Syntax</h4>
5+
replace(<i>string,wrap_length[,delimiter_string]</i>)</p>
6+
7+
<p><h4>Arguments</h4>
8+
<!-- List args for functions here-->
9+
<i> string</i> &rarr; is string. The string to be wrapped.<br>
10+
<i> wrap_length</i> &rarr; is number. If positive, the number represents the ideal maximum number of characters to wrap; if negative, the number represents the minimum number of characters to wrap.<br>
11+
<i> delimiter_string</i> &rarr; is string. The delimiter string to wrap to a new line (<u>optional</u>).<br></p>
12+
13+
<p><h4>Example</h4>
14+
<!-- Show example of function.-->
15+
wordwrap('UNIVERSITY OF QGIS',13) &rarr; 'UNIVERSITY OF\nQGIS'<br>
16+
wordwrap('UNIVERSITY OF QGIS',-3) &rarr; 'UNIVERSITY\nOF QGIS'</p>

src/core/qgsexpression.cpp

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,72 @@ static QVariant fcnTrim( const QVariantList& values, const QgsFeature* , QgsExpr
631631
return QVariant( str.trimmed() );
632632
}
633633

634+
static QVariant fcnWordwrap( const QVariantList& values, const QgsFeature* , QgsExpression* parent )
635+
{
636+
if ( values.length() == 2 || values.length() == 3 )
637+
{
638+
QString str = getStringValue( values.at( 0 ), parent );
639+
int wrap = getIntValue( values.at( 1 ), parent );
640+
641+
if ( !str.isEmpty() && wrap != 0 )
642+
{
643+
QString newstr;
644+
QString delimiterstr;
645+
if ( values.length() == 3 ) delimiterstr = getStringValue( values.at( 2 ), parent );
646+
if ( delimiterstr.isEmpty() ) delimiterstr = " ";
647+
int delimiterlength = delimiterstr.length();
648+
649+
QStringList lines = str.split( "\n" );
650+
int strlength, strcurrent, strhit, lasthit;
651+
652+
for ( int i = 0; i < lines.size(); i++ )
653+
{
654+
strlength = lines[i].length();
655+
strcurrent = 0;
656+
strhit = 0;
657+
lasthit = 0;
658+
659+
while ( strcurrent < strlength )
660+
{
661+
// positive wrap value = desired maximum line width to wrap
662+
// negative wrap value = desired minimum line width before wrap
663+
if ( wrap > 0 )
664+
{
665+
//first try to locate delimiter backwards
666+
strhit = lines[i].lastIndexOf( delimiterstr, strcurrent + wrap );
667+
if ( strhit == lasthit || strhit == -1 )
668+
{
669+
//if no new backward delimiter found, try to locate forward
670+
strhit = lines[i].indexOf( delimiterstr, strcurrent + qAbs( wrap ) );
671+
}
672+
lasthit = strhit;
673+
}
674+
else
675+
{
676+
strhit = lines[i].indexOf( delimiterstr, strcurrent + qAbs( wrap ) );
677+
}
678+
if ( strhit > -1 )
679+
{
680+
newstr.append( lines[i].midRef( strcurrent , strhit - strcurrent ) );
681+
newstr.append( "\n" );
682+
strcurrent = strhit + delimiterlength;
683+
}
684+
else
685+
{
686+
newstr.append( lines[i].midRef( strcurrent ) );
687+
strcurrent = strlength;
688+
}
689+
}
690+
if ( i < lines.size() - 1 ) newstr.append( "\n" );
691+
}
692+
693+
return QVariant( newstr );
694+
}
695+
}
696+
697+
return QVariant();
698+
}
699+
634700
static QVariant fcnLength( const QVariantList& values, const QgsFeature* , QgsExpression* parent )
635701
{
636702
QString str = getStringValue( values.at( 0 ), parent );
@@ -1416,7 +1482,7 @@ const QStringList &QgsExpression::BuiltinFunctions()
14161482
<< "coalesce" << "regexp_match" << "$now" << "age" << "year"
14171483
<< "month" << "week" << "day" << "hour"
14181484
<< "minute" << "second" << "lower" << "upper"
1419-
<< "title" << "length" << "replace" << "trim"
1485+
<< "title" << "length" << "replace" << "trim" << "wordwrap"
14201486
<< "regexp_replace" << "regexp_substr"
14211487
<< "substr" << "concat" << "strpos" << "left"
14221488
<< "right" << "rpad" << "lpad"
@@ -1484,6 +1550,7 @@ const QList<QgsExpression::Function*> &QgsExpression::Functions()
14841550
<< new StaticFunction( "upper", 1, fcnUpper, "String" )
14851551
<< new StaticFunction( "title", 1, fcnTitle, "String" )
14861552
<< new StaticFunction( "trim", 1, fcnTrim, "String" )
1553+
<< new StaticFunction( "wordwrap", -1, fcnWordwrap, "String" )
14871554
<< new StaticFunction( "length", 1, fcnLength, "String" )
14881555
<< new StaticFunction( "replace", 3, fcnReplace, "String" )
14891556
<< new StaticFunction( "regexp_replace", 3, fcnRegexpReplace, "String" )

tests/src/core/testqgsexpression.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,11 @@ class TestQgsExpression: public QObject
315315
QTest::newRow( "title" ) << "title(' HeLlO WORLD ')" << false << QVariant( " Hello World " );
316316
QTest::newRow( "trim" ) << "trim(' Test String ')" << false << QVariant( "Test String" );
317317
QTest::newRow( "trim empty string" ) << "trim('')" << false << QVariant( "" );
318+
QTest::newRow( "wordwrap" ) << "wordwrap('university of qgis',13)" << false << QVariant( "university of\nqgis" );
319+
QTest::newRow( "wordwrap" ) << "wordwrap('university of qgis',13,' ')" << false << QVariant( "university of\nqgis" );
320+
QTest::newRow( "wordwrap" ) << "wordwrap('university of qgis',-3)" << false << QVariant( "university\nof qgis" );
321+
QTest::newRow( "wordwrap" ) << "wordwrap('university of qgis',-3,' ')" << false << QVariant( "university\nof qgis" );
322+
QTest::newRow( "wordwrap" ) << "wordwrap('university of qgis\nsupports many multiline',-5,' ')" << false << QVariant( "university\nof qgis\nsupports\nmany multiline" );
318323
QTest::newRow( "format" ) << "format('%1 %2 %3 %1', 'One', 'Two', 'Three')" << false << QVariant( "One Two Three One" );
319324

320325
// implicit conversions

0 commit comments

Comments
 (0)