154 : d(
new QQuickStyledTextPrivate(string, layout, imgTags, baseUrl, context, preloadImages, fontSizeModified))
163void QQuickStyledText::parse(
const QString &string, QTextLayout &layout,
164 QList<QQuickStyledTextImgTag*> &imgTags,
166 QQmlContext *context,
168 bool *fontSizeModified)
170 if (string.isEmpty())
172 QQuickStyledText styledText(string, layout, imgTags, baseUrl, context, preloadImages, fontSizeModified);
173 styledText.d->parse();
178 QList<QTextLayout::FormatRange> ranges;
179 QStack<QTextCharFormat> formatStack;
182 drawText.reserve(text.size());
184 updateImagePositions = !imgTags->isEmpty();
189 bool formatChanged =
false;
191 const QChar *ch = text.constData();
192 while (!ch->isNull()) {
193 if (*ch == lessThan) {
195 appendText(text, textStart, textLength, drawText);
197 drawText.append(space);
202 if (rangeStart != drawText.size() && formatStack.size()) {
204 QTextLayout::FormatRange formatRange;
205 formatRange.format = formatStack.top();
206 formatRange.start = rangeStart;
207 formatRange.length = drawText.size() - rangeStart;
208 ranges.append(formatRange);
209 formatChanged =
false;
210 }
else if (ranges.size()) {
211 ranges.last().length += drawText.size() - rangeStart;
214 rangeStart = drawText.size();
218 if (parseCloseTag(ch, text, drawText)) {
219 if (formatStack.size()) {
220 formatChanged =
true;
225 QTextCharFormat format;
226 if (formatStack.size())
227 format = formatStack.top();
228 if (parseTag(ch, text, drawText, format)) {
229 formatChanged =
true;
230 formatStack.push(format);
233 textStart = ch - text.constData() + 1;
235 }
else if (*ch == ampersand) {
237 appendText(text, textStart, textLength, drawText);
238 parseEntity(ch, text, drawText);
239 textStart = ch - text.constData() + 1;
241 }
else if (ch->isSpace()) {
243 appendText(text, textStart, textLength, drawText);
246 for (
const QChar *n = ch + 1; !n->isNull() && n->isSpace(); ++n)
249 }
else if (*ch == lineFeed) {
250 drawText.append(QChar(QChar::LineSeparator));
253 drawText.append(QChar(QChar::Nbsp));
256 textStart = ch - text.constData() + 1;
265 appendText(text, textStart, textLength, drawText);
266 if (rangeStart != drawText.size() && formatStack.size()) {
268 QTextLayout::FormatRange formatRange;
269 formatRange.format = formatStack.top();
270 formatRange.start = rangeStart;
271 formatRange.length = drawText.size() - rangeStart;
272 ranges.append(formatRange);
273 }
else if (ranges.size()) {
274 ranges.last().length += drawText.size() - rangeStart;
281 for (
int i = 0; i < imgTags->size(); ++i) {
282 const QQuickStyledTextImgTag *image = imgTags->at(i);
283 QTextLayout::FormatRange range;
284 QTextImageFormat imgFmt;
285 if (image->size.isValid()) {
286 imgFmt.setWidth(image->size.width() + QQuickStyledTextImgTag::HMargin * 2);
287 imgFmt.setHeight(image->size.height());
290 imgFmt.setVerticalAlignment(QTextCharFormat::AlignBaseline);
291 imgFmt.setObjectIndex(i);
292 range.format = imgFmt;
293 range.start = image->position;
295 ranges.append(range);
299 layout.setText(drawText);
300 layout.setFormats(ranges);
331 int tagStart = ch - textIn.constData();
333 while (!ch->isNull()) {
334 if (*ch == greaterThan) {
337 auto tag = QStringView(textIn).mid(tagStart, tagLength);
338 switch (lookupHtmlTag(tag)) {
340 format.setFontWeight(QFont::Bold);
343 textOut.append(QChar(QChar::LineSeparator));
348 format.setFontItalic(
true);
352 textOut.append(QChar::LineSeparator);
359 textOut.append(QChar::LineSeparator);
360 format.setFontFamilies(QStringList {QString::fromLatin1(
"Courier New"), QString::fromLatin1(
"courier")});
361 format.setFontFixedPitch(
true);
364 format.setFontUnderline(
true);
371 listStack.push(listItem);
380 int level = tag.at(1).digitValue();
382 textOut.append(QChar::LineSeparator);
385 setFontSize(7 - level, format);
386 format.setFontWeight(QFont::Bold);
390 format.setFontStrikeOut(
true);
393 format.setFontWeight(QFont::Bold);
396 format.setFontStrikeOut(
true);
403 listStack.push(listItem);
408 textOut.append(QChar(QChar::LineSeparator));
409 if (!listStack.isEmpty()) {
410 int count = ++listStack.top().level;
411 for (
int i = 0; i < listStack.size(); ++i)
412 textOut += QString(tabsize, QChar::Nbsp);
413 switch (listStack.top().format) {
415 textOut += QString::number(count) % QLatin1Char(
'.');
418 textOut +=
toAlpha(count,
false) % QLatin1Char(
'.');
421 textOut +=
toAlpha(count,
true) % QLatin1Char(
'.');
424 textOut +=
toRoman(count,
false) % QLatin1Char(
'.');
427 textOut +=
toRoman(count,
true) % QLatin1Char(
'.');
439 textOut += QString(2, QChar::Nbsp);
446 }
else if (ch->isSpace()) {
448 auto tag = QStringView(textIn).mid(tagStart, tagLength);
449 switch (lookupHtmlTag(tag)) {
451 return parseFontAttributes(ch, textIn, format);
453 parseOrderedListAttributes(ch, textIn);
456 parseUnorderedListAttributes(ch, textIn);
459 return parseAnchorAttributes(ch, textIn, format);
461 parseImageAttributes(ch, textIn, textOut);
464 if (*ch == greaterThan || ch->isNull())
468 }
else if (*ch != slash) {
547 int entityStart = ch - textIn.constData();
548 int entityLength = 0;
549 while (!ch->isNull()) {
550 if (*ch == QLatin1Char(
';')) {
551 auto entity = QStringView(textIn).mid(entityStart, entityLength);
552#if QT_CONFIG(texthtmlparser)
553 const QString parsedEntity = QTextHtmlParser::parseEntity(entity);
554 if (!parsedEntity.isNull())
555 textOut += parsedEntity;
558 qCWarning(lcStyledText) <<
"StyledText doesn't support entity" << entity;
560 }
else if (*ch == QLatin1Char(
' ')) {
561 auto entity = QStringView(textIn).mid(entityStart - 1, entityLength + 1);
562 textOut += entity + *ch;
573 std::pair<QStringView,QStringView> attr;
575 attr = parseAttribute(ch, textIn);
576 if (is_equal_ignoring_case(attr.first, QLatin1String(
"color"))) {
578 format.setForeground(QColor::fromString(attr.second));
579 }
else if (is_equal_ignoring_case(attr.first, QLatin1String(
"size"))) {
581 int size = attr.second.toInt();
582 if (attr.second.at(0) == QLatin1Char(
'-') || attr.second.at(0) == QLatin1Char(
'+'))
584 if (size >= 1 && size <= 7)
585 setFontSize(size, format);
587 }
while (!ch->isNull() && !attr.first.isEmpty());
601 std::pair<QStringView,QStringView> attr;
603 attr = parseAttribute(ch, textIn);
604 if (is_equal_ignoring_case(attr.first, QLatin1String(
"type"))) {
606 if (attr.second == QLatin1String(
"a"))
608 else if (attr.second == QLatin1String(
"A"))
610 else if (attr.second == QLatin1String(
"i"))
612 else if (attr.second == QLatin1String(
"I"))
615 }
while (!ch->isNull() && !attr.first.isEmpty());
617 listStack.push(listItem);
630 std::pair<QStringView,QStringView> attr;
632 attr = parseAttribute(ch, textIn);
633 if (is_equal_ignoring_case(attr.first, QLatin1String(
"type"))) {
635 if (is_equal_ignoring_case(attr.second, QLatin1String(
"disc")))
637 else if (is_equal_ignoring_case(attr.second, QLatin1String(
"square")))
640 }
while (!ch->isNull() && !attr.first.isEmpty());
642 listStack.push(listItem);
667 QQuickStyledTextImgTag *image =
new QQuickStyledTextImgTag;
668 image->position = textOut.size();
670 std::pair<QStringView,QStringView> attr;
672 attr = parseAttribute(ch, textIn);
673 if (is_equal_ignoring_case(attr.first, QLatin1String(
"src"))) {
674 image->url = QUrl(attr.second.toString());
675 }
else if (is_equal_ignoring_case(attr.first, QLatin1String(
"width"))) {
677 int v = attr.second.toInt(&ok);
679 image->size.setWidth(v);
681 qCWarning(lcStyledText) <<
"Invalid width provided for <img>";
682 }
else if (is_equal_ignoring_case(attr.first, QLatin1String(
"height"))) {
684 int v = attr.second.toInt(&ok);
686 image->size.setHeight(v);
688 qCWarning(lcStyledText) <<
"Invalid height provided for <img>";
689 }
else if (is_equal_ignoring_case(attr.first, QLatin1String(
"align"))) {
690 if (is_equal_ignoring_case(attr.second, QLatin1String(
"top"))) {
691 image->align = QQuickStyledTextImgTag::Top;
692 }
else if (is_equal_ignoring_case(attr.second, QLatin1String(
"middle"))) {
693 image->align = QQuickStyledTextImgTag::Middle;
696 }
while (!ch->isNull() && !attr.first.isEmpty());
702 QUrl url = baseUrl.resolved(image->url);
703 if (url.isLocalFile()) {
704 image->pix.reset(
new QQuickPixmap(context->engine(), url, QRect(), image->size));
705 if (image->pix && image->pix->isReady()) {
706 image->size = image->pix->implicitSize();
714 if (!image->url.isValid()) {
716 qCWarning(lcStyledText) <<
"StyledText - Invalid base url in img tag";
720 imgTags->append(image);
724 QQuickStyledTextImgTag *image = imgTags->value(nbImages);
725 image->position = textOut.size();
726 std::pair<QStringView,QStringView> attr;
728 attr = parseAttribute(ch, textIn);
729 }
while (!ch->isNull() && !attr.first.isEmpty());
733 textOut += QChar::ObjectReplacementCharacter;
741 int attrStart = ch - textIn.constData();
743 while (!ch->isNull()) {
744 if (*ch == greaterThan) {
746 }
else if (*ch == equals) {
748 if (*ch != singleQuote && *ch != doubleQuote) {
749 while (*ch != greaterThan && !ch->isNull())
756 auto attr = QStringView(textIn).mid(attrStart, attrLength);
757 QStringView val = parseValue(ch, textIn);
759 return std::pair<QStringView,QStringView>(attr,val);
767 return std::pair<QStringView,QStringView>();
801 QString result = QLatin1String(
"?");
804 QByteArray romanNumeral;
806 static const char romanSymbolsLower[] =
"iiivixxxlxcccdcmmmm";
807 static const char romanSymbolsUpper[] =
"IIIVIXXXLXCCCDCMMMM";
808 QByteArray romanSymbols;
810 romanSymbols = QByteArray::fromRawData(romanSymbolsLower,
sizeof(romanSymbolsLower));
812 romanSymbols = QByteArray::fromRawData(romanSymbolsUpper,
sizeof(romanSymbolsUpper));
814 int c[] = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
816 for (
int i = 12; i >= 0; n %= c[i], i--) {
819 int startDigit = i + (i + 3) / 4;
829 romanNumeral.append(romanSymbols.mid(startDigit, numDigits));
832 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)
#define QQUICKSTYLEDPARSER_COORD_LIMIT