19 #include <QStringList> 20 #include <QTextBoundaryFinder> 21 #include <QRegularExpression> 26 if (
string.isEmpty() )
29 switch ( capitalization )
35 return string.toUpper();
38 return string.toLower();
42 QString temp = string;
44 QTextBoundaryFinder wordSplitter( QTextBoundaryFinder::Word,
string.constData(),
string.length(),
nullptr, 0 );
45 QTextBoundaryFinder letterSplitter( QTextBoundaryFinder::Grapheme,
string.constData(),
string.length(),
nullptr, 0 );
47 wordSplitter.setPosition( 0 );
49 while ( ( first && wordSplitter.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
50 || wordSplitter.toNextBoundary() >= 0 )
53 letterSplitter.setPosition( wordSplitter.position() );
54 letterSplitter.toNextBoundary();
55 QString substr =
string.mid( wordSplitter.position(), letterSplitter.position() - wordSplitter.position() );
56 temp.replace( wordSplitter.position(), substr.length(), substr.toUpper() );
65 static QStringList smallWords;
66 static QStringList newPhraseSeparators;
67 static QRegularExpression splitWords;
68 if ( smallWords.empty() )
70 smallWords = QObject::tr(
"a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|s|the|to|vs.|vs|via" ).split(
'|' );
71 newPhraseSeparators = QObject::tr(
".|:" ).split(
'|' );
72 splitWords = QRegularExpression( QStringLiteral(
"\\b" ), QRegularExpression::UseUnicodePropertiesOption );
75 const QStringList parts =
string.split( splitWords, QString::SkipEmptyParts );
77 bool firstWord =
true;
79 int lastWord = parts.count() - 1;
80 for (
const QString &word : qgis::as_const( parts ) )
82 if ( newPhraseSeparators.contains( word.trimmed() ) )
87 else if ( firstWord || ( i == lastWord ) || !smallWords.contains( word ) )
89 result += word.at( 0 ).toUpper() + word.mid( 1 );
103 result.remove(
' ' );
114 for (
int i = 0; i <
string.size(); ++i )
116 QChar ch =
string.at( i );
117 if ( ch.unicode() > 160 )
118 encoded += QStringLiteral(
"&#%1;" ).arg( static_cast< int >( ch.unicode() ) );
119 else if ( ch.unicode() == 38 )
120 encoded += QStringLiteral(
"&" );
121 else if ( ch.unicode() == 60 )
122 encoded += QStringLiteral(
"<" );
123 else if ( ch.unicode() == 62 )
124 encoded += QStringLiteral(
">" );
133 int length1 = string1.length();
134 int length2 = string2.length();
137 if ( string1.isEmpty() )
141 else if ( string2.isEmpty() )
147 QString s1( caseSensitive ? string1 : string1.toLower() );
148 QString s2( caseSensitive ? string2 : string2.toLower() );
150 const QChar *s1Char = s1.constData();
151 const QChar *s2Char = s2.constData();
154 int commonPrefixLen = 0;
155 while ( length1 > 0 && length2 > 0 && *s1Char == *s2Char )
165 while ( length1 > 0 && length2 > 0 && s1.at( commonPrefixLen + length1 - 1 ) == s2.at( commonPrefixLen + length2 - 1 ) )
176 else if ( length2 == 0 )
182 if ( length1 > length2 )
185 std::swap( length1, length2 );
190 col.fill( 0, length2 + 1 );
191 QVector< int > prevCol;
192 prevCol.reserve( length2 + 1 );
193 for (
int i = 0; i < length2 + 1; ++i )
197 const QChar *s2start = s2Char;
198 for (
int i = 0; i < length1; ++i )
202 for (
int j = 0; j < length2; ++j )
204 col[j + 1] = std::min( std::min( 1 + col[j], 1 + prevCol[1 + j] ), prevCol[j] + ( ( *s1Char == *s2Char ) ? 0 : 1 ) );
210 return prevCol[length2];
215 if ( string1.isEmpty() || string2.isEmpty() )
222 QString s1( caseSensitive ? string1 : string1.toLower() );
223 QString s2( caseSensitive ? string2 : string2.toLower() );
231 int *currentScores =
new int [ s2.length()];
232 int *previousScores =
new int [ s2.length()];
233 int maxCommonLength = 0;
234 int lastMaxBeginIndex = 0;
236 const QChar *s1Char = s1.constData();
237 const QChar *s2Char = s2.constData();
238 const QChar *s2Start = s2Char;
240 for (
int i = 0; i < s1.length(); ++i )
242 for (
int j = 0; j < s2.length(); ++j )
244 if ( *s1Char != *s2Char )
246 currentScores[j] = 0;
250 if ( i == 0 || j == 0 )
252 currentScores[j] = 1;
256 currentScores[j] = 1 + previousScores[j - 1];
259 if ( maxCommonLength < currentScores[j] )
261 maxCommonLength = currentScores[j];
262 lastMaxBeginIndex = i;
267 std::swap( currentScores, previousScores );
271 delete [] currentScores;
272 delete [] previousScores;
273 return string1.mid( lastMaxBeginIndex - maxCommonLength + 1, maxCommonLength );
278 if ( string1.isEmpty() && string2.isEmpty() )
284 if ( string1.length() != string2.length() )
291 QString s1( caseSensitive ? string1 : string1.toLower() );
292 QString s2( caseSensitive ? string2 : string2.toLower() );
301 const QChar *s1Char = s1.constData();
302 const QChar *s2Char = s2.constData();
304 for (
int i = 0; i < string1.length(); ++i )
306 if ( *s1Char != *s2Char )
317 if (
string.isEmpty() )
320 QString tmp =
string.toUpper();
323 QChar *char1 = tmp.data();
324 QChar *char2 = tmp.data();
326 for (
int i = 0; i < tmp.length(); ++i, ++char2 )
328 if ( ( *char2 ).unicode() >= 0x41 && ( *char2 ).unicode() <= 0x5A && ( i == 0 || ( ( *char2 ).unicode() != 0x41 && ( *char2 ).unicode() != 0x45
329 && ( *char2 ).unicode() != 0x48 && ( *char2 ).unicode() != 0x49
330 && ( *char2 ).unicode() != 0x4F && ( *char2 ).unicode() != 0x55
331 && ( *char2 ).unicode() != 0x57 && ( *char2 ).unicode() != 0x59 ) ) )
338 tmp.truncate( outLen );
340 QChar *tmpChar = tmp.data();
342 for (
int i = 1; i < tmp.length(); ++i, ++tmpChar )
344 switch ( ( *tmpChar ).unicode() )
350 tmp.replace( i, 1, QChar( 0x31 ) );
361 tmp.replace( i, 1, QChar( 0x32 ) );
366 tmp.replace( i, 1, QChar( 0x33 ) );
370 tmp.replace( i, 1, QChar( 0x34 ) );
375 tmp.replace( i, 1, QChar( 0x35 ) );
379 tmp.replace( i, 1, QChar( 0x36 ) );
389 for (
int i = 1; i < tmp.length(); ++i, ++char2 )
391 if ( *char2 != *char1 )
400 tmp.truncate( outLen );
401 if ( tmp.length() < 4 )
412 QString converted = string;
416 static QRegExp urlRegEx(
"(\\b(([\\w-]+://?|www[.])[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~\\s]|/))))" );
417 static QRegExp protoRegEx(
"^(?:f|ht)tps?://|file://" );
418 static QRegExp emailRegEx(
"([\\w._%+-]+@[\\w.-]+\\.[A-Za-z]+)" );
422 while ( urlRegEx.indexIn( converted, offset ) != -1 )
425 QString url = urlRegEx.cap( 1 );
426 QString protoUrl = url;
427 if ( protoRegEx.indexIn( protoUrl ) == -1 )
429 protoUrl.prepend(
"http://" );
431 QString anchor = QStringLiteral(
"<a href=\"%1\">%2</a>" ).arg( protoUrl.toHtmlEscaped(), url.toHtmlEscaped() );
432 converted.replace( urlRegEx.pos( 1 ), url.length(), anchor );
433 offset = urlRegEx.pos( 1 ) + anchor.length();
436 while ( emailRegEx.indexIn( converted, offset ) != -1 )
439 QString email = emailRegEx.cap( 1 );
440 QString anchor = QStringLiteral(
"<a href=\"mailto:%1\">%1</a>" ).arg( email.toHtmlEscaped() );
441 converted.replace( emailRegEx.pos( 1 ), email.length(), anchor );
442 offset = emailRegEx.pos( 1 ) + anchor.length();
454 QString converted = html;
455 converted.replace( QLatin1String(
"<br>" ), QLatin1String(
"\n" ) );
456 converted.replace( QLatin1String(
"<b>" ), QLatin1String(
"**" ) );
457 converted.replace( QLatin1String(
"</b>" ), QLatin1String(
"**" ) );
459 static QRegExp hrefRegEx(
"<a\\s+href\\s*=\\s*([^<>]*)\\s*>([^<>]*)</a>" );
461 while ( hrefRegEx.indexIn( converted, offset ) != -1 )
463 QString url = hrefRegEx.cap( 1 ).replace( QStringLiteral(
"\"" ), QString() );
464 url.replace( QStringLiteral(
"'" ), QString() );
465 QString name = hrefRegEx.cap( 2 );
466 QString anchor = QStringLiteral(
"[%1](%2)" ).arg( name, url );
467 converted.replace( hrefRegEx, anchor );
468 offset = hrefRegEx.pos( 1 ) + anchor.length();
474 QString
QgsStringUtils::wordWrap(
const QString &
string,
const int length,
const bool useMaxLineLength,
const QString &customDelimiter )
476 if (
string.isEmpty() || length == 0 )
481 int delimiterLength = 0;
483 if ( !customDelimiter.isEmpty() )
485 rx.setPatternSyntax( QRegExp::FixedString );
486 rx.setPattern( customDelimiter );
487 delimiterLength = customDelimiter.length();
492 rx.setPattern( QStringLiteral(
"[\\s\\x200B]" ) );
496 const QStringList lines =
string.split(
'\n' );
497 int strLength, strCurrent, strHit, lastHit;
499 for (
int i = 0; i < lines.size(); i++ )
501 strLength = lines.at( i ).length();
506 while ( strCurrent < strLength )
510 if ( useMaxLineLength )
513 strHit = lines.at( i ).lastIndexOf( rx, strCurrent + length );
514 if ( strHit == lastHit || strHit == -1 )
517 strHit = lines.at( i ).indexOf( rx, strCurrent + std::abs( length ) );
523 strHit = lines.at( i ).indexOf( rx, strCurrent + std::abs( length ) );
527 newstr.append( lines.at( i ).midRef( strCurrent, strHit - strCurrent ) );
528 newstr.append(
'\n' );
529 strCurrent = strHit + delimiterLength;
533 newstr.append( lines.at( i ).midRef( strCurrent ) );
534 strCurrent = strLength;
537 if ( i < lines.size() - 1 )
538 newstr.append(
'\n' );
546 string =
string.replace(
',', QChar( 65040 ) ).replace( QChar( 8229 ), QChar( 65072 ) );
547 string =
string.replace( QChar( 12289 ), QChar( 65041 ) ).replace( QChar( 12290 ), QChar( 65042 ) );
548 string =
string.replace(
':', QChar( 65043 ) ).replace(
';', QChar( 65044 ) );
549 string =
string.replace(
'!', QChar( 65045 ) ).replace(
'?', QChar( 65046 ) );
550 string =
string.replace( QChar( 12310 ), QChar( 65047 ) ).replace( QChar( 12311 ), QChar( 65048 ) );
551 string =
string.replace( QChar( 8230 ), QChar( 65049 ) );
552 string =
string.replace( QChar( 8212 ), QChar( 65073 ) ).replace( QChar( 8211 ), QChar( 65074 ) );
553 string =
string.replace(
'_', QChar( 65075 ) ).replace( QChar( 65103 ), QChar( 65076 ) );
554 string =
string.replace(
'(', QChar( 65077 ) ).replace(
')', QChar( 65078 ) );
555 string =
string.replace(
'{', QChar( 65079 ) ).replace(
'}', QChar( 65080 ) );
556 string =
string.replace(
'<', QChar( 65087 ) ).replace(
'>', QChar( 65088 ) );
557 string =
string.replace(
'[', QChar( 65095 ) ).replace(
']', QChar( 65096 ) );
558 string =
string.replace( QChar( 12308 ), QChar( 65081 ) ).replace( QChar( 12309 ), QChar( 65082 ) );
559 string =
string.replace( QChar( 12304 ), QChar( 65083 ) ).replace( QChar( 12305 ), QChar( 65084 ) );
560 string =
string.replace( QChar( 12298 ), QChar( 65085 ) ).replace( QChar( 12299 ), QChar( 65086 ) );
561 string =
string.replace( QChar( 12300 ), QChar( 65089 ) ).replace( QChar( 12301 ), QChar( 65090 ) );
562 string =
string.replace( QChar( 12302 ), QChar( 65091 ) ).replace( QChar( 12303 ), QChar( 65092 ) );
568 , mReplacement( replacement )
569 , mCaseSensitive( caseSensitive )
570 , mWholeWordOnly( wholeWordOnly )
572 if ( mWholeWordOnly )
573 mRx = QRegExp( QString(
"\\b%1\\b" ).arg( mMatch ),
574 mCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
579 QString result = input;
580 if ( !mWholeWordOnly )
582 return result.replace( mMatch, mReplacement, mCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
586 return result.replace( mRx, mReplacement );
593 map.insert( QStringLiteral(
"match" ), mMatch );
594 map.insert( QStringLiteral(
"replace" ), mReplacement );
595 map.insert( QStringLiteral(
"caseSensitive" ), mCaseSensitive ?
"1" :
"0" );
596 map.insert( QStringLiteral(
"wholeWord" ), mWholeWordOnly ?
"1" :
"0" );
603 properties.value( QStringLiteral(
"replace" ) ),
604 properties.value( QStringLiteral(
"caseSensitive" ), QStringLiteral(
"0" ) ) == QLatin1String(
"1" ),
605 properties.value( QStringLiteral(
"wholeWord" ), QStringLiteral(
"0" ) ) == QLatin1String(
"1" ) );
610 QString result = input;
611 const auto constMReplacements = mReplacements;
614 result = r.process( result );
621 const auto constMReplacements = mReplacements;
625 QDomElement propEl = doc.createElement( QStringLiteral(
"replacement" ) );
626 QgsStringMap::const_iterator it = props.constBegin();
627 for ( ; it != props.constEnd(); ++it )
629 propEl.setAttribute( it.key(), it.value() );
631 elem.appendChild( propEl );
637 mReplacements.clear();
638 QDomNodeList nodelist = elem.elementsByTagName( QStringLiteral(
"replacement" ) );
639 for (
int i = 0; i < nodelist.count(); i++ )
641 QDomElement replacementElem = nodelist.at( i ).toElement();
642 QDomNamedNodeMap nodeMap = replacementElem.attributes();
645 for (
int j = 0; j < nodeMap.count(); ++j )
647 props.insert( nodeMap.item( j ).nodeName(), nodeMap.item( j ).nodeValue() );
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
A representation of a single string replacement.
static QString substituteVerticalCharacters(QString string)
Returns a string with characters having vertical representation form substituted. ...
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string...
Simple title case conversion - does not fully grammatically parse the text and uses simple rules only...
Convert the string to upper camel case. Note that this method does not unaccent characters.
QMap< QString, QString > QgsStringMap
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
void writeXml(QDomElement &elem, QDomDocument &doc) const
Writes the collection state to an XML element.
static QgsStringReplacement fromProperties(const QgsStringMap &properties)
Creates a new QgsStringReplacement from an encoded properties map.
static QString capitalize(const QString &string, Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
Convert just the first letter of each word to uppercase, leave the rest untouched.
Convert all characters to uppercase.
Capitalization
Capitalization options.
QgsStringMap properties() const
Returns a map of the replacement properties.
static QString ampersandEncode(const QString &string)
Makes a raw string safe for inclusion as a HTML/XML string literal.
QString process(const QString &input) const
Processes a given input string, applying any valid replacements which should be made.
Mixed case, ie no change.
Convert all characters to lowercase.
void readXml(const QDomElement &elem)
Reads the collection state from an XML element.
QString process(const QString &input) const
Processes a given input string, applying any valid replacements which should be made using QgsStringR...
QgsStringReplacement(const QString &match, const QString &replacement, bool caseSensitive=false, bool wholeWordOnly=false)
Constructor for QgsStringReplacement.
static QString insertLinks(const QString &string, bool *foundLinks=nullptr)
Returns a string with any URL (e.g., http(s)/ftp) and mailto: text converted to valid HTML <a ...
static QString htmlToMarkdown(const QString &html)
Convert simple HTML to markdown.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.