144 : d(
new QQuickStyledTextPrivate(string, layout, imgTags, baseUrl, context, preloadImages, fontSizeModified))
168 QVector<QTextLayout::FormatRange> ranges;
169 QStack<QTextCharFormat> formatStack;
172 drawText.reserve(text.size());
174 updateImagePositions = !imgTags->isEmpty();
179 bool formatChanged =
false;
181 const QChar *ch = text.constData();
182 while (!ch->isNull()) {
183 if (*ch == lessThan) {
185 appendText(text, textStart, textLength, drawText);
187 drawText.append(space);
192 if (rangeStart != drawText.size() && formatStack.size()) {
194 QTextLayout::FormatRange formatRange;
195 formatRange.format = formatStack.top();
196 formatRange.start = rangeStart;
197 formatRange.length = drawText.size() - rangeStart;
198 ranges.append(formatRange);
199 formatChanged =
false;
200 }
else if (ranges.size()) {
201 ranges.last().length += drawText.size() - rangeStart;
204 rangeStart = drawText.size();
208 if (parseCloseTag(ch, text, drawText)) {
209 if (formatStack.size()) {
210 formatChanged =
true;
215 QTextCharFormat format;
216 if (formatStack.size())
217 format = formatStack.top();
218 if (parseTag(ch, text, drawText, format)) {
219 formatChanged =
true;
220 formatStack.push(format);
223 textStart = ch - text.constData() + 1;
225 }
else if (*ch == ampersand) {
227 appendText(text, textStart, textLength, drawText);
228 parseEntity(ch, text, drawText);
229 textStart = ch - text.constData() + 1;
231 }
else if (ch->isSpace()) {
233 appendText(text, textStart, textLength, drawText);
236 for (
const QChar *n = ch + 1; !n->isNull() && n->isSpace(); ++n)
239 }
else if (*ch == lineFeed) {
240 drawText.append(QChar(QChar::LineSeparator));
243 drawText.append(QChar(QChar::Nbsp));
246 textStart = ch - text.constData() + 1;
255 appendText(text, textStart, textLength, drawText);
256 if (rangeStart != drawText.size() && formatStack.size()) {
258 QTextLayout::FormatRange formatRange;
259 formatRange.format = formatStack.top();
260 formatRange.start = rangeStart;
261 formatRange.length = drawText.size() - rangeStart;
262 ranges.append(formatRange);
263 }
else if (ranges.size()) {
264 ranges.last().length += drawText.size() - rangeStart;
268 layout.setText(drawText);
269 layout.setFormats(ranges);
300 int tagStart = ch - textIn.constData();
302 while (!ch->isNull()) {
303 if (*ch == greaterThan) {
306 auto tag = QStringView(textIn).mid(tagStart, tagLength);
307 const QChar char0 = tag.at(0).toLower();
308 if (char0 == QLatin1Char(
'b')) {
309 if (tagLength == 1) {
310 format.setFontWeight(QFont::Bold);
312 }
else if (tagLength == 2 && tag.at(1).toLower() == QLatin1Char(
'r')) {
313 textOut.append(QChar(QChar::LineSeparator));
318 }
else if (char0 == QLatin1Char(
'i')) {
319 if (tagLength == 1) {
320 format.setFontItalic(
true);
323 }
else if (char0 == QLatin1Char(
'p')) {
324 if (tagLength == 1) {
326 textOut.append(QChar::LineSeparator);
329 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"pre"))) {
332 textOut.append(QChar::LineSeparator);
333 format.setFontFamilies(QStringList {QString::fromLatin1(
"Courier New"), QString::fromLatin1(
"courier")});
334 format.setFontFixedPitch(
true);
337 }
else if (char0 == QLatin1Char(
'u')) {
338 if (tagLength == 1) {
339 format.setFontUnderline(
true);
341 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"ul"))) {
346 listStack.push(listItem);
348 }
else if (char0 == QLatin1Char(
'h') && tagLength == 2) {
349 int level = tag.at(1).digitValue();
350 if (level >= 1 && level <= 6) {
352 textOut.append(QChar::LineSeparator);
355 setFontSize(7 - level, format);
356 format.setFontWeight(QFont::Bold);
359 }
else if (char0 == QLatin1Char(
's')) {
360 if (tagLength == 1) {
361 format.setFontStrikeOut(
true);
363 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"strong"))) {
364 format.setFontWeight(QFont::Bold);
367 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"del"))) {
368 format.setFontStrikeOut(
true);
370 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"ol"))) {
375 listStack.push(listItem);
376 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"li"))) {
378 textOut.append(QChar(QChar::LineSeparator));
379 if (!listStack.isEmpty()) {
380 int count = ++listStack.top().level;
381 for (
int i = 0; i < listStack.size(); ++i)
382 textOut += QString(tabsize, QChar::Nbsp);
383 switch (listStack.top().format) {
385 textOut += QString::number(count) % QLatin1Char(
'.');
388 textOut +=
toAlpha(count,
false) % QLatin1Char(
'.');
391 textOut +=
toAlpha(count,
true) % QLatin1Char(
'.');
394 textOut +=
toRoman(count,
false) % QLatin1Char(
'.');
397 textOut +=
toRoman(count,
true) % QLatin1Char(
'.');
409 textOut += QString(2, QChar::Nbsp);
413 }
else if (ch->isSpace()) {
415 auto tag = QStringView(textIn).mid(tagStart, tagLength);
416 if (is_equal_ignoring_case(tag, QLatin1String(
"font")))
417 return parseFontAttributes(ch, textIn, format);
418 if (is_equal_ignoring_case(tag, QLatin1String(
"ol"))) {
419 parseOrderedListAttributes(ch, textIn);
422 if (is_equal_ignoring_case(tag, QLatin1String(
"ul"))) {
423 parseUnorderedListAttributes(ch, textIn);
426 if (is_equal_ignoring_case(tag, QLatin1String(
"a"))) {
427 return parseAnchorAttributes(ch, textIn, format);
429 if (is_equal_ignoring_case(tag, QLatin1String(
"img"))) {
430 parseImageAttributes(ch, textIn, textOut);
433 if (*ch == greaterThan || ch->isNull())
435 }
else if (*ch != slash) {
447 int tagStart = ch - textIn.constData();
449 while (!ch->isNull()) {
450 if (*ch == greaterThan) {
453 auto tag = QStringView(textIn).mid(tagStart, tagLength);
454 const QChar char0 = tag.at(0).toLower();
456 if (char0 == QLatin1Char(
'b')) {
459 else if (tag.at(1).toLower() == QLatin1Char(
'r') && tagLength == 2)
461 }
else if (char0 == QLatin1Char(
'i')) {
464 }
else if (char0 == QLatin1Char(
'a')) {
467 }
else if (char0 == QLatin1Char(
'p')) {
468 if (tagLength == 1) {
469 textOut.append(QChar::LineSeparator);
473 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"pre"))) {
476 textOut.append(QChar::LineSeparator);
481 }
else if (char0 == QLatin1Char(
'u')) {
484 else if (is_equal_ignoring_case(tag, QLatin1String(
"ul"))) {
485 if (!listStack.isEmpty()) {
487 if (!listStack.size())
488 textOut.append(QChar::LineSeparator);
492 }
else if (char0 == QLatin1Char(
'h') && tagLength == 2) {
493 textOut.append(QChar::LineSeparator);
497 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"font"))) {
499 }
else if (char0 == QLatin1Char(
's')) {
500 if (tagLength == 1) {
502 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"strong"))) {
505 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"del"))) {
507 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"ol"))) {
508 if (!listStack.isEmpty()) {
510 if (!listStack.size())
511 textOut.append(QChar::LineSeparator);
514 }
else if (is_equal_ignoring_case(tag, QLatin1String(
"li"))) {
518 }
else if (!ch->isSpace()){
555 std::pair<QStringView,QStringView> attr;
557 attr = parseAttribute(ch, textIn);
558 if (is_equal_ignoring_case(attr.first, QLatin1String(
"color"))) {
560 format.setForeground(QColor::fromString(attr.second));
561 }
else if (is_equal_ignoring_case(attr.first, QLatin1String(
"size"))) {
563 int size = attr.second.toInt();
564 if (attr.second.at(0) == QLatin1Char(
'-') || attr.second.at(0) == QLatin1Char(
'+'))
566 if (size >= 1 && size <= 7)
567 setFontSize(size, format);
569 }
while (!ch->isNull() && !attr.first.isEmpty());
583 std::pair<QStringView,QStringView> attr;
585 attr = parseAttribute(ch, textIn);
586 if (is_equal_ignoring_case(attr.first, QLatin1String(
"type"))) {
588 if (attr.second == QLatin1String(
"a"))
590 else if (attr.second == QLatin1String(
"A"))
592 else if (attr.second == QLatin1String(
"i"))
594 else if (attr.second == QLatin1String(
"I"))
597 }
while (!ch->isNull() && !attr.first.isEmpty());
599 listStack.push(listItem);
612 std::pair<QStringView,QStringView> attr;
614 attr = parseAttribute(ch, textIn);
615 if (is_equal_ignoring_case(attr.first, QLatin1String(
"type"))) {
617 if (is_equal_ignoring_case(attr.second, QLatin1String(
"disc")))
619 else if (is_equal_ignoring_case(attr.second, QLatin1String(
"square")))
622 }
while (!ch->isNull() && !attr.first.isEmpty());
624 listStack.push(listItem);
648 qreal imgWidth = 0.0;
649 QFontMetricsF fm(layout.font());
650 const qreal spaceWidth = fm.horizontalAdvance(QChar::Nbsp);
651 const bool trailingSpace = textOut.endsWith(space);
654 QQuickStyledTextImgTag *image =
new QQuickStyledTextImgTag;
655 image->position = textOut.size() + (trailingSpace ? 0 : 1);
657 std::pair<QStringView,QStringView> attr;
659 attr = parseAttribute(ch, textIn);
660 if (is_equal_ignoring_case(attr.first, QLatin1String(
"src"))) {
661 image->url = QUrl(attr.second.toString());
662 }
else if (is_equal_ignoring_case(attr.first, QLatin1String(
"width"))) {
663 image->size.setWidth(attr.second.toString().toInt());
664 }
else if (is_equal_ignoring_case(attr.first, QLatin1String(
"height"))) {
665 image->size.setHeight(attr.second.toString().toInt());
666 }
else if (is_equal_ignoring_case(attr.first, QLatin1String(
"align"))) {
667 if (is_equal_ignoring_case(attr.second, QLatin1String(
"top"))) {
668 image->align = QQuickStyledTextImgTag::Top;
669 }
else if (is_equal_ignoring_case(attr.second, QLatin1String(
"middle"))) {
670 image->align = QQuickStyledTextImgTag::Middle;
673 }
while (!ch->isNull() && !attr.first.isEmpty());
679 QUrl url = baseUrl.resolved(image->url);
680 if (url.isLocalFile()) {
681 image->pix.reset(
new QQuickPixmap(context->engine(), url, QRect(), image->size));
682 if (image->pix && image->pix->isReady()) {
683 image->size = image->pix->implicitSize();
691 if (!image->url.isValid()) {
693 qCWarning(lcStyledText) <<
"StyledText - Invalid base url in img tag";
695 imgWidth = image->size.width();
696 image->offset = -
std::fmod(imgWidth, spaceWidth) / 2.0;
697 imgTags->append(image);
702 QQuickStyledTextImgTag *image = imgTags->value(nbImages);
703 image->position = textOut.size() + (trailingSpace ? 0 : 1);
704 imgWidth = image->size.width();
705 image->offset = -
std::fmod(imgWidth, spaceWidth) / 2.0;
706 std::pair<QStringView,QStringView> attr;
708 attr = parseAttribute(ch, textIn);
709 }
while (!ch->isNull() && !attr.first.isEmpty());
713 QString padding(qFloor(imgWidth / spaceWidth), QChar::Nbsp);
715 textOut += QLatin1Char(
' ');
716 textOut += padding + QLatin1Char(
' ');
723 int attrStart = ch - textIn.constData();
725 while (!ch->isNull()) {
726 if (*ch == greaterThan) {
728 }
else if (*ch == equals) {
730 if (*ch != singleQuote && *ch != doubleQuote) {
731 while (*ch != greaterThan && !ch->isNull())
738 auto attr = QStringView(textIn).mid(attrStart, attrLength);
739 QStringView val = parseValue(ch, textIn);
741 return std::pair<QStringView,QStringView>(attr,val);
749 return std::pair<QStringView,QStringView>();
783 QString result = QLatin1String(
"?");
786 QByteArray romanNumeral;
788 static const char romanSymbolsLower[] =
"iiivixxxlxcccdcmmmm";
789 static const char romanSymbolsUpper[] =
"IIIVIXXXLXCCCDCMMMM";
790 QByteArray romanSymbols;
792 romanSymbols = QByteArray::fromRawData(romanSymbolsLower,
sizeof(romanSymbolsLower));
794 romanSymbols = QByteArray::fromRawData(romanSymbolsUpper,
sizeof(romanSymbolsUpper));
796 int c[] = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
798 for (
int i = 12; i >= 0; n %= c[i], i--) {
801 int startDigit = i + (i + 3) / 4;
811 romanNumeral.append(romanSymbols.mid(startDigit, numDigits));
814 result = QString::fromLatin1(romanNumeral);
QList< QQuickStyledTextImgTag * > * imgTags
bool parseCloseTag(const QChar *&ch, const QString &textIn, QString &textOut)
void parseImageAttributes(const QChar *&ch, const QString &textIn, QString &textOut)
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)
std::pair< QStringView, QStringView > parseAttribute(const QChar *&ch, const QString &textIn)
void parseEntity(const QChar *&ch, const QString &textIn, QString &textOut)
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)
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)