Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickstyledtext.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QStack>
5#include <QVector>
6#include <QPainter>
7#include <QTextLayout>
8#include <QDebug>
9#include <qmath.h>
10#include "qquickstyledtext_p.h"
11#include <QQmlContext>
12#include <QtGui/private/qtexthtmlparser_p.h>
13
14Q_LOGGING_CATEGORY(lcStyledText, "qt.quick.styledtext")
15
16/*
17 QQuickStyledText supports few tags:
18
19 <b></b> - bold
20 <del></del> - strike out (removed content)
21 <s></s> - strike out (no longer accurate or no longer relevant content)
22 <strong></strong> - bold
23 <i></i> - italic
24 <br> - new line
25 <p> - paragraph
26 <u> - underlined text
27 <font color="color_name" size="1-7"></font>
28 <h1> to <h6> - headers
29 <a href=""> - anchor
30 <ol type="">, <ul type=""> and <li> - ordered and unordered lists
31 <pre></pre> - preformated
32 <img src=""> - images
33
34 The opening and closing tags must be correctly nested.
35*/
36
38
39Q_GUI_EXPORT int qt_defaultDpi();
40
42{
43public:
44 enum ListType { Ordered, Unordered };
45 enum ListFormat { Bullet, Disc, Square, Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman };
46
52
54 QList<QQuickStyledTextImgTag*> &imgTags,
55 const QUrl &baseUrl,
57 bool preloadImages,
58 bool *fontSizeModified)
59 : text(t), layout(l), imgTags(&imgTags), baseFont(layout.font()), baseUrl(baseUrl),
60 fontSizeModified(fontSizeModified), context(context), preloadImages(preloadImages)
61 {
62 }
63
64 void parse();
65 void appendText(const QString &textIn, int start, int length, QString &textOut);
66 bool parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format);
67 bool parseCloseTag(const QChar *&ch, const QString &textIn, QString &textOut);
68 void parseEntity(const QChar *&ch, const QString &textIn, QString &textOut);
69 bool parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format);
70 bool parseOrderedListAttributes(const QChar *&ch, const QString &textIn);
71 bool parseUnorderedListAttributes(const QChar *&ch, const QString &textIn);
72 bool parseAnchorAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format);
73 void parseImageAttributes(const QChar *&ch, const QString &textIn, QString &textOut);
74 QPair<QStringView,QStringView> parseAttribute(const QChar *&ch, const QString &textIn);
75 QStringView parseValue(const QChar *&ch, const QString &textIn);
76 void setFontSize(int size, QTextCharFormat &format);
77
78 inline void skipSpace(const QChar *&ch) {
79 while (ch->isSpace() && !ch->isNull())
80 ++ch;
81 }
82
83 static QString toAlpha(int value, bool upper);
84 static QString toRoman(int value, bool upper);
85
88 QList<QQuickStyledTextImgTag*> *imgTags;
90 QStack<List> listStack;
94 int nbImages = 0;
95 bool hasNewLine = true;
96 bool updateImagePositions = false;
97 bool preFormat = false;
98 bool prependSpace = false;
99 bool hasSpace = true;
101
102 static const QChar lessThan;
103 static const QChar greaterThan;
104 static const QChar equals;
105 static const QChar singleQuote;
106 static const QChar doubleQuote;
107 static const QChar slash;
108 static const QChar ampersand;
109 static const QChar bullet;
110 static const QChar disc;
111 static const QChar square;
112 static const QChar lineFeed;
113 static const QChar space;
114 static const int tabsize = 6;
115};
116
129
130namespace {
131bool is_equal_ignoring_case(QStringView s1, QLatin1StringView s2) noexcept
132{
133 return s1.compare(s2, Qt::CaseInsensitive) == 0;
134}
135}
136
137QQuickStyledText::QQuickStyledText(const QString &string, QTextLayout &layout,
138 QList<QQuickStyledTextImgTag*> &imgTags,
139 const QUrl &baseUrl,
141 bool preloadImages,
142 bool *fontSizeModified)
143 : d(new QQuickStyledTextPrivate(string, layout, imgTags, baseUrl, context, preloadImages, fontSizeModified))
144{
145}
146
147QQuickStyledText::~QQuickStyledText()
148{
149 delete d;
150}
151
153 QList<QQuickStyledTextImgTag*> &imgTags,
154 const QUrl &baseUrl,
156 bool preloadImages,
157 bool *fontSizeModified)
158{
159 if (string.isEmpty())
160 return;
161 QQuickStyledText styledText(string, layout, imgTags, baseUrl, context, preloadImages, fontSizeModified);
162 styledText.d->parse();
163}
164
166{
167 QVector<QTextLayout::FormatRange> ranges;
168 QStack<QTextCharFormat> formatStack;
169
172
174
175 int textStart = 0;
176 int textLength = 0;
177 int rangeStart = 0;
178 bool formatChanged = false;
179
180 const QChar *ch = text.constData();
181 while (!ch->isNull()) {
182 if (*ch == lessThan) {
183 if (textLength) {
184 appendText(text, textStart, textLength, drawText);
185 } else if (prependSpace) {
186 drawText.append(space);
187 prependSpace = false;
188 hasSpace = true;
189 }
190
191 if (rangeStart != drawText.size() && formatStack.size()) {
192 if (formatChanged) {
193 QTextLayout::FormatRange formatRange;
194 formatRange.format = formatStack.top();
195 formatRange.start = rangeStart;
196 formatRange.length = drawText.size() - rangeStart;
197 ranges.append(formatRange);
198 formatChanged = false;
199 } else if (ranges.size()) {
200 ranges.last().length += drawText.size() - rangeStart;
201 }
202 }
203 rangeStart = drawText.size();
204 ++ch;
205 if (*ch == slash) {
206 ++ch;
207 if (parseCloseTag(ch, text, drawText)) {
208 if (formatStack.size()) {
209 formatChanged = true;
210 formatStack.pop();
211 }
212 }
213 } else {
215 if (formatStack.size())
216 format = formatStack.top();
217 if (parseTag(ch, text, drawText, format)) {
218 formatChanged = true;
219 formatStack.push(format);
220 }
221 }
222 textStart = ch - text.constData() + 1;
223 textLength = 0;
224 } else if (*ch == ampersand) {
225 ++ch;
226 appendText(text, textStart, textLength, drawText);
228 textStart = ch - text.constData() + 1;
229 textLength = 0;
230 } else if (ch->isSpace()) {
231 if (textLength)
232 appendText(text, textStart, textLength, drawText);
233 if (!preFormat) {
235 for (const QChar *n = ch + 1; !n->isNull() && n->isSpace(); ++n)
236 ch = n;
237 hasNewLine = false;
238 } else if (*ch == lineFeed) {
239 drawText.append(QChar(QChar::LineSeparator));
240 hasNewLine = true;
241 } else {
242 drawText.append(QChar(QChar::Nbsp));
243 hasNewLine = false;
244 }
245 textStart = ch - text.constData() + 1;
246 textLength = 0;
247 } else {
248 ++textLength;
249 }
250 if (!ch->isNull())
251 ++ch;
252 }
253 if (textLength)
254 appendText(text, textStart, textLength, drawText);
255 if (rangeStart != drawText.size() && formatStack.size()) {
256 if (formatChanged) {
257 QTextLayout::FormatRange formatRange;
258 formatRange.format = formatStack.top();
259 formatRange.start = rangeStart;
260 formatRange.length = drawText.size() - rangeStart;
261 ranges.append(formatRange);
262 } else if (ranges.size()) {
263 ranges.last().length += drawText.size() - rangeStart;
264 }
265 }
266
268 layout.setFormats(ranges);
269}
270
271void QQuickStyledTextPrivate::appendText(const QString &textIn, int start, int length, QString &textOut)
272{
273 if (prependSpace)
274 textOut.append(space);
275 textOut.append(QStringView(textIn).mid(start, length));
276 prependSpace = false;
277 hasSpace = false;
278 hasNewLine = false;
279}
280
281//
282// Calculates and sets the correct font size in points
283// depending on the size multiplier and base font.
284//
286{
287 static const qreal scaling[] = { 0.7, 0.8, 1.0, 1.2, 1.5, 2.0, 2.4 };
288 if (baseFont.pointSizeF() != -1)
289 format.setFontPointSize(baseFont.pointSize() * scaling[size - 1]);
290 else
291 format.setFontPointSize(baseFont.pixelSize() * qreal(72.) / qreal(qt_defaultDpi()) * scaling[size - 1]);
292 *fontSizeModified = true;
293}
294
296{
297 skipSpace(ch);
298
299 int tagStart = ch - textIn.constData();
300 int tagLength = 0;
301 while (!ch->isNull()) {
302 if (*ch == greaterThan) {
303 if (tagLength == 0)
304 return false;
305 auto tag = QStringView(textIn).mid(tagStart, tagLength);
306 const QChar char0 = tag.at(0).toLower();
307 if (char0 == QLatin1Char('b')) {
308 if (tagLength == 1) {
309 format.setFontWeight(QFont::Bold);
310 return true;
311 } else if (tagLength == 2 && tag.at(1).toLower() == QLatin1Char('r')) {
312 textOut.append(QChar(QChar::LineSeparator));
313 hasSpace = true;
314 prependSpace = false;
315 return false;
316 }
317 } else if (char0 == QLatin1Char('i')) {
318 if (tagLength == 1) {
319 format.setFontItalic(true);
320 return true;
321 }
322 } else if (char0 == QLatin1Char('p')) {
323 if (tagLength == 1) {
324 if (!hasNewLine)
325 textOut.append(QChar::LineSeparator);
326 hasSpace = true;
327 prependSpace = false;
328 } else if (is_equal_ignoring_case(tag, QLatin1String("pre"))) {
329 preFormat = true;
330 if (!hasNewLine)
331 textOut.append(QChar::LineSeparator);
332 format.setFontFamilies(QStringList {QString::fromLatin1("Courier New"), QString::fromLatin1("courier")});
333 format.setFontFixedPitch(true);
334 return true;
335 }
336 } else if (char0 == QLatin1Char('u')) {
337 if (tagLength == 1) {
338 format.setFontUnderline(true);
339 return true;
340 } else if (is_equal_ignoring_case(tag, QLatin1String("ul"))) {
341 List listItem;
342 listItem.level = 0;
343 listItem.type = Unordered;
344 listItem.format = Bullet;
345 listStack.push(listItem);
346 }
347 } else if (char0 == QLatin1Char('h') && tagLength == 2) {
348 int level = tag.at(1).digitValue();
349 if (level >= 1 && level <= 6) {
350 if (!hasNewLine)
351 textOut.append(QChar::LineSeparator);
352 hasSpace = true;
353 prependSpace = false;
355 format.setFontWeight(QFont::Bold);
356 return true;
357 }
358 } else if (char0 == QLatin1Char('s')) {
359 if (tagLength == 1) {
360 format.setFontStrikeOut(true);
361 return true;
362 } else if (is_equal_ignoring_case(tag, QLatin1String("strong"))) {
363 format.setFontWeight(QFont::Bold);
364 return true;
365 }
366 } else if (is_equal_ignoring_case(tag, QLatin1String("del"))) {
367 format.setFontStrikeOut(true);
368 return true;
369 } else if (is_equal_ignoring_case(tag, QLatin1String("ol"))) {
370 List listItem;
371 listItem.level = 0;
372 listItem.type = Ordered;
373 listItem.format = Decimal;
374 listStack.push(listItem);
375 } else if (is_equal_ignoring_case(tag, QLatin1String("li"))) {
376 if (!hasNewLine)
377 textOut.append(QChar(QChar::LineSeparator));
378 if (!listStack.isEmpty()) {
379 int count = ++listStack.top().level;
380 for (int i = 0; i < listStack.size(); ++i)
381 textOut += QString(tabsize, QChar::Nbsp);
382 switch (listStack.top().format) {
383 case Decimal:
384 textOut += QString::number(count) % QLatin1Char('.');
385 break;
386 case LowerAlpha:
387 textOut += toAlpha(count, false) % QLatin1Char('.');
388 break;
389 case UpperAlpha:
390 textOut += toAlpha(count, true) % QLatin1Char('.');
391 break;
392 case LowerRoman:
393 textOut += toRoman(count, false) % QLatin1Char('.');
394 break;
395 case UpperRoman:
396 textOut += toRoman(count, true) % QLatin1Char('.');
397 break;
398 case Bullet:
399 textOut += bullet;
400 break;
401 case Disc:
402 textOut += disc;
403 break;
404 case Square:
405 textOut += square;
406 break;
407 }
408 textOut += QString(2, QChar::Nbsp);
409 }
410 }
411 return false;
412 } else if (ch->isSpace()) {
413 // may have params.
414 auto tag = QStringView(textIn).mid(tagStart, tagLength);
415 if (is_equal_ignoring_case(tag, QLatin1String("font")))
416 return parseFontAttributes(ch, textIn, format);
417 if (is_equal_ignoring_case(tag, QLatin1String("ol"))) {
419 return false; // doesn't modify format
420 }
421 if (is_equal_ignoring_case(tag, QLatin1String("ul"))) {
423 return false; // doesn't modify format
424 }
425 if (is_equal_ignoring_case(tag, QLatin1String("a"))) {
426 return parseAnchorAttributes(ch, textIn, format);
427 }
428 if (is_equal_ignoring_case(tag, QLatin1String("img"))) {
429 parseImageAttributes(ch, textIn, textOut);
430 return false;
431 }
432 if (*ch == greaterThan || ch->isNull())
433 continue;
434 } else if (*ch != slash) {
435 tagLength++;
436 }
437 ++ch;
438 }
439 return false;
440}
441
442bool QQuickStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &textIn, QString &textOut)
443{
444 skipSpace(ch);
445
446 int tagStart = ch - textIn.constData();
447 int tagLength = 0;
448 while (!ch->isNull()) {
449 if (*ch == greaterThan) {
450 if (tagLength == 0)
451 return false;
452 auto tag = QStringView(textIn).mid(tagStart, tagLength);
453 const QChar char0 = tag.at(0).toLower();
454 hasNewLine = false;
455 if (char0 == QLatin1Char('b')) {
456 if (tagLength == 1)
457 return true;
458 else if (tag.at(1).toLower() == QLatin1Char('r') && tagLength == 2)
459 return false;
460 } else if (char0 == QLatin1Char('i')) {
461 if (tagLength == 1)
462 return true;
463 } else if (char0 == QLatin1Char('a')) {
464 if (tagLength == 1)
465 return true;
466 } else if (char0 == QLatin1Char('p')) {
467 if (tagLength == 1) {
468 textOut.append(QChar::LineSeparator);
469 hasNewLine = true;
470 hasSpace = true;
471 return false;
472 } else if (is_equal_ignoring_case(tag, QLatin1String("pre"))) {
473 preFormat = false;
474 if (!hasNewLine)
475 textOut.append(QChar::LineSeparator);
476 hasNewLine = true;
477 hasSpace = true;
478 return true;
479 }
480 } else if (char0 == QLatin1Char('u')) {
481 if (tagLength == 1)
482 return true;
483 else if (is_equal_ignoring_case(tag, QLatin1String("ul"))) {
484 if (!listStack.isEmpty()) {
485 listStack.pop();
486 if (!listStack.size())
487 textOut.append(QChar::LineSeparator);
488 }
489 return false;
490 }
491 } else if (char0 == QLatin1Char('h') && tagLength == 2) {
492 textOut.append(QChar::LineSeparator);
493 hasNewLine = true;
494 hasSpace = true;
495 return true;
496 } else if (is_equal_ignoring_case(tag, QLatin1String("font"))) {
497 return true;
498 } else if (char0 == QLatin1Char('s')) {
499 if (tagLength == 1) {
500 return true;
501 } else if (is_equal_ignoring_case(tag, QLatin1String("strong"))) {
502 return true;
503 }
504 } else if (is_equal_ignoring_case(tag, QLatin1String("del"))) {
505 return true;
506 } else if (is_equal_ignoring_case(tag, QLatin1String("ol"))) {
507 if (!listStack.isEmpty()) {
508 listStack.pop();
509 if (!listStack.size())
510 textOut.append(QChar::LineSeparator);
511 }
512 return false;
513 } else if (is_equal_ignoring_case(tag, QLatin1String("li"))) {
514 return false;
515 }
516 return false;
517 } else if (!ch->isSpace()){
518 tagLength++;
519 }
520 ++ch;
521 }
522
523 return false;
524}
525
526void QQuickStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textIn, QString &textOut)
527{
528 int entityStart = ch - textIn.constData();
529 int entityLength = 0;
530 while (!ch->isNull()) {
531 if (*ch == QLatin1Char(';')) {
532 auto entity = QStringView(textIn).mid(entityStart, entityLength);
533#if QT_CONFIG(texthtmlparser)
534 const QString parsedEntity = QTextHtmlParser::parseEntity(entity);
535 if (!parsedEntity.isNull())
536 textOut += parsedEntity;
537 else
538#endif
539 qCWarning(lcStyledText) << "StyledText doesn't support entity" << entity;
540 return;
541 } else if (*ch == QLatin1Char(' ')) {
542 auto entity = QStringView(textIn).mid(entityStart - 1, entityLength + 1);
543 textOut += entity + *ch;
544 return;
545 }
546 ++entityLength;
547 ++ch;
548 }
549}
550
552{
553 bool valid = false;
554 QPair<QStringView,QStringView> attr;
555 do {
556 attr = parseAttribute(ch, textIn);
557 if (is_equal_ignoring_case(attr.first, QLatin1String("color"))) {
558 valid = true;
559 format.setForeground(QColor::fromString(attr.second));
560 } else if (is_equal_ignoring_case(attr.first, QLatin1String("size"))) {
561 valid = true;
562 int size = attr.second.toInt();
563 if (attr.second.at(0) == QLatin1Char('-') || attr.second.at(0) == QLatin1Char('+'))
564 size += 3;
565 if (size >= 1 && size <= 7)
567 }
568 } while (!ch->isNull() && !attr.first.isEmpty());
569
570 return valid;
571}
572
574{
575 bool valid = false;
576
577 List listItem;
578 listItem.level = 0;
579 listItem.type = Ordered;
580 listItem.format = Decimal;
581
582 QPair<QStringView,QStringView> attr;
583 do {
584 attr = parseAttribute(ch, textIn);
585 if (is_equal_ignoring_case(attr.first, QLatin1String("type"))) {
586 valid = true;
587 if (attr.second == QLatin1String("a"))
588 listItem.format = LowerAlpha;
589 else if (attr.second == QLatin1String("A"))
590 listItem.format = UpperAlpha;
591 else if (attr.second == QLatin1String("i"))
592 listItem.format = LowerRoman;
593 else if (attr.second == QLatin1String("I"))
594 listItem.format = UpperRoman;
595 }
596 } while (!ch->isNull() && !attr.first.isEmpty());
597
598 listStack.push(listItem);
599 return valid;
600}
601
603{
604 bool valid = false;
605
606 List listItem;
607 listItem.level = 0;
608 listItem.type = Unordered;
609 listItem.format = Bullet;
610
611 QPair<QStringView,QStringView> attr;
612 do {
613 attr = parseAttribute(ch, textIn);
614 if (is_equal_ignoring_case(attr.first, QLatin1String("type"))) {
615 valid = true;
616 if (is_equal_ignoring_case(attr.second, QLatin1String("disc")))
617 listItem.format = Disc;
618 else if (is_equal_ignoring_case(attr.second, QLatin1String("square")))
619 listItem.format = Square;
620 }
621 } while (!ch->isNull() && !attr.first.isEmpty());
622
623 listStack.push(listItem);
624 return valid;
625}
626
628{
629 bool valid = false;
630
631 QPair<QStringView,QStringView> attr;
632 do {
633 attr = parseAttribute(ch, textIn);
634 if (is_equal_ignoring_case(attr.first, QLatin1String("href"))) {
635 format.setAnchorHref(attr.second.toString());
636 format.setAnchor(true);
637 format.setFontUnderline(true);
638 valid = true;
639 }
640 } while (!ch->isNull() && !attr.first.isEmpty());
641
642 return valid;
643}
644
646{
647 qreal imgWidth = 0.0;
649 const qreal spaceWidth = fm.horizontalAdvance(QChar::Nbsp);
650 const bool trailingSpace = textOut.endsWith(space);
651
654 image->position = textOut.size() + (trailingSpace ? 0 : 1);
655
656 QPair<QStringView,QStringView> attr;
657 do {
658 attr = parseAttribute(ch, textIn);
659 if (is_equal_ignoring_case(attr.first, QLatin1String("src"))) {
660 image->url = QUrl(attr.second.toString());
661 } else if (is_equal_ignoring_case(attr.first, QLatin1String("width"))) {
662 image->size.setWidth(attr.second.toString().toInt());
663 } else if (is_equal_ignoring_case(attr.first, QLatin1String("height"))) {
664 image->size.setHeight(attr.second.toString().toInt());
665 } else if (is_equal_ignoring_case(attr.first, QLatin1String("align"))) {
666 if (is_equal_ignoring_case(attr.second, QLatin1String("top"))) {
668 } else if (is_equal_ignoring_case(attr.second, QLatin1String("middle"))) {
670 }
671 }
672 } while (!ch->isNull() && !attr.first.isEmpty());
673
674 if (preloadImages && !image->size.isValid()) {
675 // if we don't know its size but the image is a local image,
676 // we load it in the pixmap cache and save its implicit size
677 // to avoid a relayout later on.
678 QUrl url = baseUrl.resolved(image->url);
679 if (url.isLocalFile()) {
680 image->pix.reset(new QQuickPixmap(context->engine(), url, QRect(), image->size));
681 if (image->pix && image->pix->isReady()) {
682 image->size = image->pix->implicitSize();
683 } else {
684 image->pix.reset();
685 }
686 }
687 }
688
689 // Return immediately if img tag has invalid url
690 if (!image->url.isValid()) {
691 delete image;
692 qCWarning(lcStyledText) << "StyledText - Invalid base url in img tag";
693 } else {
694 imgWidth = image->size.width();
695 image->offset = -std::fmod(imgWidth, spaceWidth) / 2.0;
697 }
698 } else {
699 // if we already have a list of img tags for this text
700 // we only want to update the positions of these tags.
702 image->position = textOut.size() + (trailingSpace ? 0 : 1);
703 imgWidth = image->size.width();
704 image->offset = -std::fmod(imgWidth, spaceWidth) / 2.0;
705 QPair<QStringView,QStringView> attr;
706 do {
707 attr = parseAttribute(ch, textIn);
708 } while (!ch->isNull() && !attr.first.isEmpty());
709 nbImages++;
710 }
711
712 QString padding(qFloor(imgWidth / spaceWidth), QChar::Nbsp);
713 if (!trailingSpace)
714 textOut += QLatin1Char(' ');
715 textOut += padding + QLatin1Char(' ');
716}
717
718QPair<QStringView,QStringView> QQuickStyledTextPrivate::parseAttribute(const QChar *&ch, const QString &textIn)
719{
720 skipSpace(ch);
721
722 int attrStart = ch - textIn.constData();
723 int attrLength = 0;
724 while (!ch->isNull()) {
725 if (*ch == greaterThan) {
726 break;
727 } else if (*ch == equals) {
728 ++ch;
729 if (*ch != singleQuote && *ch != doubleQuote) {
730 while (*ch != greaterThan && !ch->isNull())
731 ++ch;
732 break;
733 }
734 ++ch;
735 if (!attrLength)
736 break;
737 auto attr = QStringView(textIn).mid(attrStart, attrLength);
738 QStringView val = parseValue(ch, textIn);
739 if (!val.isEmpty())
740 return QPair<QStringView,QStringView>(attr,val);
741 break;
742 } else {
743 ++attrLength;
744 }
745 ++ch;
746 }
747
748 return QPair<QStringView,QStringView>();
749}
750
752{
753 int valStart = ch - textIn.constData();
754 int valLength = 0;
755 while (*ch != singleQuote && *ch != doubleQuote && !ch->isNull()) {
756 ++valLength;
757 ++ch;
758 }
759 if (ch->isNull())
760 return QStringView();
761 ++ch; // skip quote
762
763 return QStringView(textIn).mid(valStart, valLength);
764}
765
767{
768 const char baseChar = upper ? 'A' : 'a';
769
771 int c = value;
772 while (c > 0) {
773 c--;
774 result.prepend(QChar(baseChar + (c % 26)));
775 c /= 26;
776 }
777 return result;
778}
779
781{
783 // works for up to 4999 items
784 if (value < 5000) {
785 QByteArray romanNumeral;
786
787 static const char romanSymbolsLower[] = "iiivixxxlxcccdcmmmm";
788 static const char romanSymbolsUpper[] = "IIIVIXXXLXCCCDCMMMM";
789 QByteArray romanSymbols;
790 if (!upper)
791 romanSymbols = QByteArray::fromRawData(romanSymbolsLower, sizeof(romanSymbolsLower));
792 else
793 romanSymbols = QByteArray::fromRawData(romanSymbolsUpper, sizeof(romanSymbolsUpper));
794
795 int c[] = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
796 int n = value;
797 for (int i = 12; i >= 0; n %= c[i], i--) {
798 int q = n / c[i];
799 if (q > 0) {
800 int startDigit = i + (i + 3) / 4;
801 int numDigits;
802 if (i % 4) {
803 if ((i - 2) % 4)
804 numDigits = 2;
805 else
806 numDigits = 1;
807 }
808 else
809 numDigits = q;
810 romanNumeral.append(romanSymbols.mid(startDigit, numDigits));
811 }
812 }
813 result = QString::fromLatin1(romanNumeral);
814 }
815 return result;
816}
817
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:409
\inmodule QtCore
static QColor fromString(QAnyStringView name) noexcept
Definition qcolor.cpp:980
\reentrant \inmodule QtGui
\reentrant
Definition qfont.h:22
int pixelSize() const
Returns the pixel size of the font if it was set with setPixelSize().
Definition qfont.cpp:1074
int pointSize() const
Returns the point size of the font.
Definition qfont.cpp:884
qreal pointSizeF() const
Returns the point size of the font.
Definition qfont.cpp:1034
@ Bold
Definition qfont.h:70
bool isEmpty() const noexcept
Definition qlist.h:401
T value(qsizetype i) const
Definition qlist.h:664
void append(parameter_type t)
Definition qlist.h:458
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
QQmlEngine * engine() const
Return the context's QQmlEngine, or \nullptr if the context has no QQmlEngine or the QQmlEngine was d...
bool parseCloseTag(const QChar *&ch, const QString &textIn, QString &textOut)
static const QChar singleQuote
QPair< QStringView, QStringView > parseAttribute(const QChar *&ch, const QString &textIn)
void parseImageAttributes(const QChar *&ch, const QString &textIn, QString &textOut)
static const QChar lessThan
void setFontSize(int size, QTextCharFormat &format)
bool parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format)
bool parseAnchorAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format)
static const QChar ampersand
static const QChar greaterThan
void parseEntity(const QChar *&ch, const QString &textIn, QString &textOut)
static const QChar doubleQuote
static const QChar lineFeed
QStringView parseValue(const QChar *&ch, const QString &textIn)
static QString toRoman(int value, bool upper)
bool parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format)
bool parseOrderedListAttributes(const QChar *&ch, const QString &textIn)
QList< QQuickStyledTextImgTag * > * imgTags
void appendText(const QString &textIn, int start, int length, QString &textOut)
void skipSpace(const QChar *&ch)
QQuickStyledTextPrivate(const QString &t, QTextLayout &l, QList< QQuickStyledTextImgTag * > &imgTags, const QUrl &baseUrl, QQmlContext *context, bool preloadImages, bool *fontSizeModified)
static QString toAlpha(int value, bool upper)
bool parseUnorderedListAttributes(const QChar *&ch, const QString &textIn)
static void parse(const QString &string, QTextLayout &layout, QList< QQuickStyledTextImgTag * > &imgTags, const QUrl &baseUrl, QQmlContext *context, bool preloadImages, bool *fontSizeModified)
\inmodule QtCore\reentrant
Definition qrect.h:30
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
Definition qstring.h:1325
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1246
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
\reentrant
Definition qtextlayout.h:70
void setFormats(const QList< FormatRange > &overrides)
void setText(const QString &string)
Sets the layout's text to the given string.
QFont font() const
Returns the current font that is used for the layout, or a default font if none is set.
\inmodule QtCore
Definition qurl.h:94
bool isLocalFile() const
Definition qurl.cpp:3445
QUrl resolved(const QUrl &relative) const
Returns the result of the merge of this URL with relative.
Definition qurl.cpp:2725
paint drawText(0, 50, "Hi")
QString text
Combined button and popup list for selecting options.
@ CaseInsensitive
Definition image.cpp:4
static void * context
AudioChannelLayoutTag tag
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
int qFloor(T v)
Definition qmath.h:42
GLenum GLuint GLint level
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLuint start
GLfloat n
GLint GLsizei GLsizei GLenum format
const GLubyte * c
GLuint GLfloat * val
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLsizei const GLchar *const * string
[0]
Definition qopenglext.h:694
QT_BEGIN_NAMESPACE Q_GUI_EXPORT int qt_defaultDpi()
Definition qfont.cpp:140
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define s2
static void parseAttribute(void *value, void *extraData)
Definition main.cpp:199
double qreal
Definition qtypes.h:187
static int numDigits(qlonglong n)
QUrl url("example.com")
[constructor-url-reference]
QUrl baseUrl
QVBoxLayout * layout
\inmodule QtCore \reentrant
Definition qchar.h:18
QTextCharFormat format