KCoreAddons

ktexttohtml.cpp
1 /*
2  SPDX-FileCopyrightText: 2002 Dave Corrie <[email protected]>
3  SPDX-FileCopyrightText: 2014 Daniel Vr├ítil <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "ktexttohtml.h"
9 #include "ktexttohtml_p.h"
10 #include "ktexttohtmlemoticonsinterface.h"
11 
12 #include <QCoreApplication>
13 #include <QFile>
14 #include <QPluginLoader>
15 #include <QRegularExpression>
16 #include <QStringList>
17 
18 #include <limits.h>
19 
20 static KTextToHTMLEmoticonsInterface *s_emoticonsInterface = nullptr;
21 
22 static void loadEmoticonsPlugin()
23 {
24  static bool triedLoadPlugin = false;
25  if (!triedLoadPlugin) {
26  triedLoadPlugin = true;
27 
28  // Check if QGuiApplication::platformName property exists. This is a
29  // hackish way of determining whether we are running QGuiApplication,
30  // because we cannot load the FrameworkIntegration plugin into a
31  // QCoreApplication, as it would crash immediately
32  if (qApp->metaObject()->indexOfProperty("platformName") > -1) {
33  QPluginLoader lib(QStringLiteral("kf" QT_STRINGIFY(QT_VERSION_MAJOR) "/KEmoticonsIntegrationPlugin"));
34  QObject *rootObj = lib.instance();
35  if (rootObj) {
36  s_emoticonsInterface = rootObj->property(KTEXTTOHTMLEMOTICONS_PROPERTY).value<KTextToHTMLEmoticonsInterface *>();
37  }
38  }
39  }
40  if (!s_emoticonsInterface) {
41  s_emoticonsInterface = new KTextToHTMLEmoticonsDummy();
42  }
43 }
44 
45 KTextToHTMLHelper::KTextToHTMLHelper(const QString &plainText, int pos, int maxUrlLen, int maxAddressLen)
46  : mText(plainText)
47  , mMaxUrlLen(maxUrlLen)
48  , mMaxAddressLen(maxAddressLen)
49  , mPos(pos)
50 {
51 }
52 
53 KTextToHTMLEmoticonsInterface *KTextToHTMLHelper::emoticonsInterface() const
54 {
55  if (!s_emoticonsInterface) {
56  loadEmoticonsPlugin();
57  }
58  return s_emoticonsInterface;
59 }
60 
61 QString KTextToHTMLHelper::getEmailAddress()
62 {
64 
65  if (mPos < mText.length() && mText.at(mPos) == QLatin1Char('@')) {
66  // the following characters are allowed in a dot-atom (RFC 2822):
67  // a-z A-Z 0-9 . ! # $ % & ' * + - / = ? ^ _ ` { | } ~
68  const QString allowedSpecialChars = QStringLiteral(".!#$%&'*+-/=?^_`{|}~");
69 
70  // determine the local part of the email address
71  int start = mPos - 1;
72  while (start >= 0 && mText.at(start).unicode() < 128
73  && (mText.at(start).isLetterOrNumber() //
74  || mText.at(start) == QLatin1Char('@') // allow @ to find invalid email addresses
75  || allowedSpecialChars.indexOf(mText.at(start)) != -1)) {
76  if (mText.at(start) == QLatin1Char('@')) {
77  return QString(); // local part contains '@' -> no email address
78  }
79  --start;
80  }
81  ++start;
82  // we assume that an email address starts with a letter or a digit
83  while ((start < mPos) && !mText.at(start).isLetterOrNumber()) {
84  ++start;
85  }
86  if (start == mPos) {
87  return QString(); // local part is empty -> no email address
88  }
89 
90  // determine the domain part of the email address
91  int dotPos = INT_MAX;
92  int end = mPos + 1;
93  while (end < mText.length()
94  && (mText.at(end).isLetterOrNumber() //
95  || mText.at(end) == QLatin1Char('@') // allow @ to find invalid email addresses
96  || mText.at(end) == QLatin1Char('.') //
97  || mText.at(end) == QLatin1Char('-'))) {
98  if (mText.at(end) == QLatin1Char('@')) {
99  return QString(); // domain part contains '@' -> no email address
100  }
101  if (mText.at(end) == QLatin1Char('.')) {
102  dotPos = qMin(dotPos, end); // remember index of first dot in domain
103  }
104  ++end;
105  }
106  // we assume that an email address ends with a letter or a digit
107  while ((end > mPos) && !mText.at(end - 1).isLetterOrNumber()) {
108  --end;
109  }
110  if (end == mPos) {
111  return QString(); // domain part is empty -> no email address
112  }
113  if (dotPos >= end) {
114  return QString(); // domain part doesn't contain a dot
115  }
116 
117  if (end - start > mMaxAddressLen) {
118  return QString(); // too long -> most likely no email address
119  }
120  address = mText.mid(start, end - start);
121 
122  mPos = end - 1;
123  }
124  return address;
125 }
126 
127 QString KTextToHTMLHelper::getPhoneNumber()
128 {
129  if (!mText.at(mPos).isDigit() && mText.at(mPos) != QLatin1Char('+')) {
130  return {};
131  }
132 
133  const QString allowedBeginSeparators = QStringLiteral(" \r\t\n:");
134  if (mPos > 0 && !allowedBeginSeparators.contains(mText.at(mPos - 1))) {
135  return {};
136  }
137 
138  // this isn't 100% accurate, we filter stuff below that is too hard to capture with a regexp
139  static const QRegularExpression telPattern(QStringLiteral(R"([+0](( |( ?[/-] ?)?)\(?\d+\)?+){6,30})"));
140  const auto match = telPattern.match(mText,
141  mPos,
143 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
145 #else
146  QRegularExpression::AnchorAtOffsetMatchOption
147 #endif
148  );
149  if (match.hasMatch()) {
150  QStringView matchedText = match.capturedView();
151  // check for maximum number of digits (15), see https://en.wikipedia.org/wiki/Telephone_numbering_plan
152  const int digitsCount = std::count_if(matchedText.cbegin(), matchedText.cend(), [](const QChar c) {
153  return c.isDigit();
154  });
155 
156  if (digitsCount > 15) {
157  return {};
158  }
159 
160  // only one / is allowed, otherwise we trigger on dates
161  if (matchedText.count(QLatin1Char('/')) > 1) {
162  return {};
163  }
164 
165  // parenthesis need to be balanced, and must not be nested
166  int openIdx = -1;
167  for (int i = 0, size = matchedText.size(); i < size; ++i) {
168  const QChar ch = matchedText.at(i);
169  if ((ch == QLatin1Char('(') && openIdx >= 0) || (ch == QLatin1Char(')') && openIdx < 0)) {
170  return {};
171  }
172 
173  if (ch == QLatin1Char('(')) {
174  openIdx = i;
175  } else if (ch == QLatin1Char(')')) {
176  openIdx = -1;
177  }
178  }
179 
180  if (openIdx > 0) {
181  matchedText.truncate(openIdx - 1);
182  matchedText = matchedText.trimmed();
183  }
184 
185  // check if there's a plausible separator at the end
186  const int matchedTextLength = matchedText.size();
187  const int endIdx = mPos + matchedTextLength;
188  if (endIdx < mText.size() && !QStringView(u" \r\t\n,.").contains(mText.at(endIdx))) {
189  return {};
190  }
191 
192  mPos += matchedTextLength - 1;
193  return matchedText.toString();
194  }
195  return {};
196 }
197 
198 static QString normalizePhoneNumber(const QString &str)
199 {
200  QString res;
201  res.reserve(str.size());
202  for (const auto c : str) {
203  if (c.isDigit() || c == QLatin1Char('+')) {
204  res.push_back(c);
205  }
206  }
207  return res;
208 }
209 
210 // The following characters are allowed in a dot-atom (RFC 2822):
211 // a-z A-Z 0-9 . ! # $ % & ' * + - / = ? ^ _ ` { | } ~
212 static const char s_allowedSpecialChars[] = ".!#$%&'*+-/=?^_`{|}~";
213 
214 bool KTextToHTMLHelper::atUrl() const
215 {
216  // The character directly before the URL must not be a letter, a number or
217  // any other character allowed in a dot-atom (RFC 2822).
218  if (mPos > 0) {
219  const auto chBefore = mText.at(mPos - 1);
220  if (chBefore.isLetterOrNumber() || QLatin1String(s_allowedSpecialChars).contains(chBefore)) {
221  return false;
222  }
223  }
224 
225  const auto segment = QStringView(mText).mid(mPos);
226  /* clang-format off */
227  return segment.startsWith(QLatin1String("http://"))
228  || segment.startsWith(QLatin1String("https://"))
229  || segment.startsWith(QLatin1String("vnc://"))
230  || segment.startsWith(QLatin1String("fish://"))
231  || segment.startsWith(QLatin1String("ftp://"))
232  || segment.startsWith(QLatin1String("ftps://"))
233  || segment.startsWith(QLatin1String("sftp://"))
234  || segment.startsWith(QLatin1String("smb://"))
235  || segment.startsWith(QLatin1String("irc://"))
236  || segment.startsWith(QLatin1String("ircs://"))
237  || segment.startsWith(QLatin1String("mailto:"))
238  || segment.startsWith(QLatin1String("www."))
239  || segment.startsWith(QLatin1String("ftp."))
240  || segment.startsWith(QLatin1String("file://"))
241  || segment.startsWith(QLatin1String("news:"))
242  || segment.startsWith(QLatin1String("tel:"))
243  || segment.startsWith(QLatin1String("xmpp:"));
244  /* clang-format on */
245 }
246 
247 bool KTextToHTMLHelper::isEmptyUrl(const QString &url) const
248 {
249  /* clang-format off */
250  return url.isEmpty()
251  || url == QLatin1String("http://")
252  || url == QLatin1String("https://")
253  || url == QLatin1String("fish://")
254  || url == QLatin1String("ftp://")
255  || url == QLatin1String("ftps://")
256  || url == QLatin1String("sftp://")
257  || url == QLatin1String("smb://")
258  || url == QLatin1String("vnc://")
259  || url == QLatin1String("irc://")
260  || url == QLatin1String("ircs://")
261  || url == QLatin1String("mailto")
262  || url == QLatin1String("mailto:")
263  || url == QLatin1String("www")
264  || url == QLatin1String("ftp")
265  || url == QLatin1String("news:")
266  || url == QLatin1String("news://")
267  || url == QLatin1String("tel")
268  || url == QLatin1String("tel:")
269  || url == QLatin1String("xmpp:");
270  /* clang-format on */
271 }
272 
273 QString KTextToHTMLHelper::getUrl(bool *badurl)
274 {
275  QString url;
276  if (atUrl()) {
277  // NOTE: see http://tools.ietf.org/html/rfc3986#appendix-A and especially appendix-C
278  // Appendix-C mainly says, that when extracting URLs from plain text, line breaks shall
279  // be allowed and should be ignored when the URI is extracted.
280 
281  // This implementation follows this recommendation and
282  // allows the URL to be enclosed within different kind of brackets/quotes
283  // If an URL is enclosed, whitespace characters are allowed and removed, otherwise
284  // the URL ends with the first whitespace
285  // Also, if the URL is enclosed in brackets, the URL itself is not allowed
286  // to contain the closing bracket, as this would be detected as the end of the URL
287 
288  QChar beforeUrl;
289  QChar afterUrl;
290 
291  // detect if the url has been surrounded by brackets or quotes
292  if (mPos > 0) {
293  beforeUrl = mText.at(mPos - 1);
294 
295  /*if ( beforeUrl == '(' ) {
296  afterUrl = ')';
297  } else */
298  if (beforeUrl == QLatin1Char('[')) {
299  afterUrl = QLatin1Char(']');
300  } else if (beforeUrl == QLatin1Char('<')) {
301  afterUrl = QLatin1Char('>');
302  } else if (beforeUrl == QLatin1Char('>')) { // for e.g. <link>http://.....</link>
303  afterUrl = QLatin1Char('<');
304  } else if (beforeUrl == QLatin1Char('"')) {
305  afterUrl = QLatin1Char('"');
306  }
307  }
308  url.reserve(mMaxUrlLen); // avoid allocs
309  int start = mPos;
310  bool previousCharIsSpace = false;
311  bool previousCharIsADoubleQuote = false;
312  bool previousIsAnAnchor = false;
313  /* clang-format off */
314  while (mPos < mText.length() //
315  && (mText.at(mPos).isPrint() || mText.at(mPos).isSpace())
316  && ((afterUrl.isNull() && !mText.at(mPos).isSpace())
317  || (!afterUrl.isNull() && mText.at(mPos) != afterUrl))) {
318  if (!previousCharIsSpace
319  && mText.at(mPos) == QLatin1Char('<')
320  && (mPos + 1) < mText.length()) { /* clang-format on */
321  // Fix Bug #346132: allow "http://www.foo.bar<http://foo.bar/>"
322  // < inside a URL is not allowed, however there is a test which
323  // checks that "http://some<Host>/path" should be allowed
324  // Therefore: check if what follows is another URL and if so, stop here
325  mPos++;
326  if (atUrl()) {
327  mPos--;
328  break;
329  }
330  mPos--;
331  }
332  if (!previousCharIsSpace && (mText.at(mPos) == QLatin1Char(' ')) && ((mPos + 1) < mText.length())) {
333  // Fix kmail bug: allow "http://www.foo.bar http://foo.bar/"
334  // Therefore: check if what follows is another URL and if so, stop here
335  mPos++;
336  if (atUrl()) {
337  mPos--;
338  break;
339  }
340  mPos--;
341  }
342  if (mText.at(mPos).isSpace()) {
343  previousCharIsSpace = true;
344  } else if (!previousIsAnAnchor && mText.at(mPos) == QLatin1Char('[')) {
345  break;
346  } else if (!previousIsAnAnchor && mText.at(mPos) == QLatin1Char(']')) {
347  break;
348  } else { // skip whitespace
349  if (previousCharIsSpace && mText.at(mPos) == QLatin1Char('<')) {
350  url.append(QLatin1Char(' '));
351  break;
352  }
353  previousCharIsSpace = false;
354  if (mText.at(mPos) == QLatin1Char('>') && previousCharIsADoubleQuote) {
355  // it's an invalid url
356  if (badurl) {
357  *badurl = true;
358  }
359  return QString();
360  }
361  if (mText.at(mPos) == QLatin1Char('"')) {
362  previousCharIsADoubleQuote = true;
363  } else {
364  previousCharIsADoubleQuote = false;
365  }
366  if (mText.at(mPos) == QLatin1Char('#')) {
367  previousIsAnAnchor = true;
368  }
369  url.append(mText.at(mPos));
370  if (url.length() > mMaxUrlLen) {
371  break;
372  }
373  }
374 
375  ++mPos;
376  }
377 
378  if (isEmptyUrl(url) || (url.length() > mMaxUrlLen)) {
379  mPos = start;
380  url.clear();
381  return url;
382  } else {
383  --mPos;
384  }
385  }
386 
387  // HACK: This is actually against the RFC. However, most people don't properly escape the URL in
388  // their text with "" or <>. That leads to people writing an url, followed immediately by
389  // a dot to finish the sentence. That would lead the parser to include the dot in the url,
390  // even though that is not wanted. So work around that here.
391  // Most real-life URLs hopefully don't end with dots or commas.
392  const QString wordBoundaries = QStringLiteral(".,:!?)>");
393  if (url.length() > 1) {
394  do {
395  if (wordBoundaries.contains(url.at(url.length() - 1))) {
396  url.chop(1);
397  --mPos;
398  } else {
399  break;
400  }
401  } while (url.length() > 1);
402  }
403  return url;
404 }
405 
406 QString KTextToHTMLHelper::highlightedText()
407 {
408  // formating symbols must be prepended with a whitespace
409  if ((mPos > 0) && !mText.at(mPos - 1).isSpace()) {
410  return QString();
411  }
412 
413  const QChar ch = mText.at(mPos);
414  if (ch != QLatin1Char('/') && ch != QLatin1Char('*') && ch != QLatin1Char('_') && ch != QLatin1Char('-')) {
415  return QString();
416  }
417 
418  QRegularExpression re(QStringLiteral("\\%1([^\\s|^\\%1].*[^\\s|^\\%1])\\%1").arg(ch));
419  re.setPatternOptions(QRegularExpression::InvertedGreedinessOption);
420  const auto match = re.match(mText,
421  mPos,
423 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
425 #else
426  QRegularExpression::AnchorAtOffsetMatchOption
427 #endif
428  );
429 
430  if (match.hasMatch()) {
431  if (match.capturedStart() == mPos) {
432  int length = match.capturedLength();
433  // there must be a whitespace after the closing formating symbol
434  if (mPos + length < mText.length() && !mText.at(mPos + length).isSpace()) {
435  return QString();
436  }
437  mPos += length - 1;
438  switch (ch.toLatin1()) {
439  case '*':
440  return QLatin1String("<b>*") + match.capturedView(1) + QLatin1String("*</b>");
441  case '_':
442  return QLatin1String("<u>_") + match.capturedView(1) + QLatin1String("_</u>");
443  case '/':
444  return QLatin1String("<i>/") + match.capturedView(1) + QLatin1String("/</i>");
445  case '-':
446  return QLatin1String("<s>-") + match.capturedView(1) + QLatin1String("-</s>");
447  }
448  }
449  }
450  return QString();
451 }
452 
453 QString KTextToHTML::convertToHtml(const QString &plainText, const KTextToHTML::Options &flags, int maxUrlLen, int maxAddressLen)
454 {
455  KTextToHTMLHelper helper(plainText, 0, maxUrlLen, maxAddressLen);
456 
457  QString str;
458  QString result(static_cast<QChar *>(nullptr), helper.mText.length() * 2);
459  QChar ch;
460  int x;
461  bool startOfLine = true;
462 
463  for (helper.mPos = 0, x = 0; helper.mPos < helper.mText.length(); ++helper.mPos, ++x) {
464  ch = helper.mText.at(helper.mPos);
465  if (flags & PreserveSpaces) {
466  if (ch == QLatin1Char(' ')) {
467  if (helper.mPos + 1 < helper.mText.length()) {
468  if (helper.mText.at(helper.mPos + 1) != QLatin1Char(' ')) {
469  // A single space, make it breaking if not at the start or end of the line
470  const bool endOfLine = helper.mText.at(helper.mPos + 1) == QLatin1Char('\n');
471  if (!startOfLine && !endOfLine) {
472  result += QLatin1Char(' ');
473  } else {
474  result += QLatin1String("&nbsp;");
475  }
476  } else {
477  // Whitespace of more than one space, make it all non-breaking
478  while (helper.mPos < helper.mText.length() && helper.mText.at(helper.mPos) == QLatin1Char(' ')) {
479  result += QLatin1String("&nbsp;");
480  ++helper.mPos;
481  ++x;
482  }
483 
484  // We incremented once to often, undo that
485  --helper.mPos;
486  --x;
487  }
488  } else {
489  // Last space in the text, it is non-breaking
490  result += QLatin1String("&nbsp;");
491  }
492 
493  if (startOfLine) {
494  startOfLine = false;
495  }
496  continue;
497  } else if (ch == QLatin1Char('\t')) {
498  do {
499  result += QLatin1String("&nbsp;");
500  ++x;
501  } while ((x & 7) != 0);
502  --x;
503  startOfLine = false;
504  continue;
505  }
506  }
507  if (ch == QLatin1Char('\n')) {
508  result += QLatin1String("<br />\n"); // Keep the \n, so apps can figure out the quoting levels correctly.
509  startOfLine = true;
510  x = -1;
511  continue;
512  }
513 
514  startOfLine = false;
515  if (ch == QLatin1Char('&')) {
516  result += QLatin1String("&amp;");
517  } else if (ch == QLatin1Char('"')) {
518  result += QLatin1String("&quot;");
519  } else if (ch == QLatin1Char('<')) {
520  result += QLatin1String("&lt;");
521  } else if (ch == QLatin1Char('>')) {
522  result += QLatin1String("&gt;");
523  } else {
524  const int start = helper.mPos;
525  if (!(flags & IgnoreUrls)) {
526  bool badUrl = false;
527  str = helper.getUrl(&badUrl);
528  if (badUrl) {
529  QString resultBadUrl;
530  for (const QChar chBadUrl : std::as_const(helper.mText)) {
531  if (chBadUrl == QLatin1Char('&')) {
532  resultBadUrl += QLatin1String("&amp;");
533  } else if (chBadUrl == QLatin1Char('"')) {
534  resultBadUrl += QLatin1String("&quot;");
535  } else if (chBadUrl == QLatin1Char('<')) {
536  resultBadUrl += QLatin1String("&lt;");
537  } else if (chBadUrl == QLatin1Char('>')) {
538  resultBadUrl += QLatin1String("&gt;");
539  } else {
540  resultBadUrl += chBadUrl;
541  }
542  }
543  return resultBadUrl;
544  }
545  if (!str.isEmpty()) {
546  QString hyperlink;
547  if (str.startsWith(QLatin1String("www."))) {
548  hyperlink = QLatin1String("http://") + str;
549  } else if (str.startsWith(QLatin1String("ftp."))) {
550  hyperlink = QLatin1String("ftp://") + str;
551  } else {
552  hyperlink = str;
553  }
554  result += QLatin1String("<a href=\"") + hyperlink + QLatin1String("\">") + str.toHtmlEscaped() + QLatin1String("</a>");
555  x += helper.mPos - start;
556  continue;
557  }
558  str = helper.getEmailAddress();
559  if (!str.isEmpty()) {
560  // len is the length of the local part
561  int len = str.indexOf(QLatin1Char('@'));
562  QString localPart = str.left(len);
563 
564  // remove the local part from the result (as '&'s have been expanded to
565  // &amp; we have to take care of the 4 additional characters per '&')
566  result.truncate(result.length() - len - (localPart.count(QLatin1Char('&')) * 4));
567  x -= len;
568 
569  result += QLatin1String("<a href=\"mailto:") + str + QLatin1String("\">") + str + QLatin1String("</a>");
570  x += str.length() - 1;
571  continue;
572  }
573  if (flags & ConvertPhoneNumbers) {
574  str = helper.getPhoneNumber();
575  if (!str.isEmpty()) {
576  result += QLatin1String("<a href=\"tel:") + normalizePhoneNumber(str) + QLatin1String("\">") + str + QLatin1String("</a>");
577  x += str.length() - 1;
578  continue;
579  }
580  }
581  }
582  if (flags & HighlightText) {
583  str = helper.highlightedText();
584  if (!str.isEmpty()) {
585  result += str;
586  x += helper.mPos - start;
587  continue;
588  }
589  }
590  result += ch;
591  }
592  }
593 
594  if (flags & ReplaceSmileys) {
595  const QStringList exclude = {QStringLiteral("(c)"), QStringLiteral("(C)"), QStringLiteral("&gt;:-("), QStringLiteral("&gt;:("), QStringLiteral("(B)"),
596  QStringLiteral("(b)"), QStringLiteral("(P)"), QStringLiteral("(p)"), QStringLiteral("(O)"), QStringLiteral("(o)"),
597  QStringLiteral("(D)"), QStringLiteral("(d)"), QStringLiteral("(E)"), QStringLiteral("(e)"), QStringLiteral("(K)"),
598  QStringLiteral("(k)"), QStringLiteral("(I)"), QStringLiteral("(i)"), QStringLiteral("(L)"), QStringLiteral("(l)"),
599  QStringLiteral("(8)"), QStringLiteral("(T)"), QStringLiteral("(t)"), QStringLiteral("(G)"), QStringLiteral("(g)"),
600  QStringLiteral("(F)"), QStringLiteral("(f)"), QStringLiteral("(H)"), QStringLiteral("8)"), QStringLiteral("(N)"),
601  QStringLiteral("(n)"), QStringLiteral("(Y)"), QStringLiteral("(y)"), QStringLiteral("(U)"), QStringLiteral("(u)"),
602  QStringLiteral("(W)"), QStringLiteral("(w)"), QStringLiteral("(6)")};
603 
604  result = helper.emoticonsInterface()->parseEmoticons(result, true, exclude);
605  }
606 
607  return result;
608 }
QChar at(qsizetype n) const const
void truncate(int position)
int size() const const
QString toHtmlEscaped() const const
T value() const const
void clear()
QStringView mid(qsizetype start) const const
Q_SCRIPTABLE Q_NOREPLY void start()
void chop(int n)
@ ReplaceSmileys
Replace text emoticons smileys by emoticons images.
Definition: ktexttohtml.h:37
QStringView::const_iterator cbegin() const const
void reserve(int size)
QStringView::const_iterator cend() const const
qsizetype size() const const
@ IgnoreUrls
Don't parse and replace any URLs.
Definition: ktexttohtml.h:42
QString toString() const const
KCOREADDONS_EXPORT QString convertToHtml(const QString &plainText, const KTextToHTML::Options &options, int maxUrlLen=4096, int maxAddressLen=255)
Converts plaintext into html.
void truncate(qsizetype length)
bool isEmpty() const const
int length() const const
void push_back(QChar ch)
@ ConvertPhoneNumbers
Replace phone numbers with tel: links.
Definition: ktexttohtml.h:54
PostalAddress address(const QVariant &location)
QStringView trimmed() const const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
@ PreserveSpaces
Preserve white-space formatting of the text.
Definition: ktexttohtml.h:28
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
@ HighlightText
Interpret text highlighting markup, like bold, underline and /italic/, and wrap them in corresponding...
Definition: ktexttohtml.h:48
int count() const const
QString left(int n) const const
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
This is the main function which does scored fuzzy matching.
const QChar at(int position) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
char toLatin1() const const
bool startsWith(QStringView str, Qt::CaseSensitivity cs) const const
bool isNull() const const
const QList< QKeySequence > & end()
QString & append(QChar ch)
QVariant property(const char *name) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Feb 7 2023 04:11:52 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.