202 svgBuffer.open(QIODevice::WriteOnly);
204 QXmlStreamReader htmlReader(htmlText);
205 QXmlStreamWriter svgWriter(&svgBuffer);
207 svgWriter.setAutoFormatting(
false);
208#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
209 QStringRef elementName;
211 QStringView elementName;
214 bool newLine =
false;
216 QString bodyEm =
"1em";
220 QString previousStyleString;
224 "span",
"font",
"b",
"strong",
"em",
"i",
"pre",
"u"
226 bool firstElement =
true;
228 while (!htmlReader.atEnd()) {
229 QXmlStreamReader::TokenType token = htmlReader.readNext();
230 QLatin1String elName = firstElement? QLatin1String(
"text"): QLatin1String(
"tspan");
232 case QXmlStreamReader::StartElement:
235 if (htmlReader.name() ==
"br") {
237 svgWriter.writeEndElement();
238#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
239 elementName = QStringRef(&
p);
241 elementName = QStringView(
p);
244 appendStyle = previousStyleString;
247 elementName = htmlReader.name();
251 if (elementName ==
"body") {
252 debugFlake <<
"\tstart Element" << elementName;
253 svgWriter.writeStartElement(elName);
254 firstElement =
false;
255 appendStyle = QString();
257 else if (elementName ==
"p") {
259 debugFlake <<
"\t\tstart Element" << elementName;
260 svgWriter.writeStartElement(elName);
261 firstElement =
false;
265 appendStyle = QString();
269 else if (elementName ==
"span") {
270 debugFlake <<
"\tstart Element" << elementName;
271 svgWriter.writeStartElement(elName);
272 firstElement =
false;
273 appendStyle = QString();
275 else if (elementName ==
"b" || elementName ==
"strong") {
276 debugFlake <<
"\tstart Element" << elementName;
277 svgWriter.writeStartElement(elName);
278 firstElement =
false;
279 appendStyle =
"font-weight:700;";
281 else if (elementName ==
"i" || elementName ==
"em") {
282 debugFlake <<
"\tstart Element" << elementName;
283 svgWriter.writeStartElement(elName);
284 firstElement =
false;
285 appendStyle =
"font-style:italic;";
287 else if (elementName ==
"u") {
288 debugFlake <<
"\tstart Element" << elementName;
289 svgWriter.writeStartElement(elName);
290 firstElement =
false;
291 appendStyle =
"text-decoration:underline";
293 else if (elementName ==
"font") {
294 debugFlake <<
"\tstart Element" << elementName;
295 svgWriter.writeStartElement(elName);
296 firstElement =
false;
297 appendStyle = QString();
298 if (htmlReader.attributes().hasAttribute(
"color")) {
299 svgWriter.writeAttribute(
"fill", htmlReader.attributes().value(
"color").toString());
302 else if (elementName ==
"pre") {
303 debugFlake <<
"\tstart Element" << elementName;
304 svgWriter.writeStartElement(elName);
305 firstElement =
false;
306 appendStyle =
"white-space:pre";
309 QXmlStreamAttributes attributes = htmlReader.attributes();
312 if (attributes.hasAttribute(
"align")) {
313 textAlign = attributes.value(
"align").toString();
316 if (attributes.hasAttribute(
"style") || !appendStyle.isEmpty()) {
317 QString filteredStyles;
318 QStringList svgStyles = QString(
"font-family font-size font-weight font-variant word-spacing text-decoration font-style font-size-adjust font-stretch direction letter-spacing").split(
" ");
319 QStringList styles = attributes.value(
"style").toString().split(
";");
320 for(
int i=0; i<styles.size(); i++) {
323 if (svgStyles.contains(QString(
style.at(0)).trimmed())) {
324 filteredStyles.append(styles.at(i)+
";");
327 if (QString(
style.at(0)).trimmed() ==
"color") {
328 filteredStyles.append(
" fill:"+
style.at(1)+
";");
331 if (QString(
style.at(0)).trimmed() ==
"text-align") {
332 textAlign = QString(
style.at(1)).trimmed();
335 if (QString(
style.at(0)).trimmed() ==
"line-height"){
336 if (
style.at(1).contains(
"%")) {
337 double percentage = QString(
style.at(1)).remove(
"%").toDouble();
338 em = QString::number(percentage/100.0)+
"em";
339 }
else if(
style.at(1).contains(
"em")) {
341 }
else if(
style.at(1).contains(
"px")) {
344 if (elementName ==
"body") {
350 if (textAlign ==
"center") {
351 filteredStyles.append(
" text-anchor:middle;");
352 }
else if (textAlign ==
"right") {
353 filteredStyles.append(
" text-anchor:end;");
354 }
else if (textAlign ==
"left"){
355 filteredStyles.append(
" text-anchor:start;");
358 filteredStyles.append(appendStyle);
360 if (!filteredStyles.isEmpty()) {
361 svgWriter.writeAttribute(
"style", filteredStyles);
362 previousStyleString = filteredStyles;
367 if (newLine && lineCount > 1) {
368 debugFlake <<
"\t\tAdvancing to the next line";
369 svgWriter.writeAttribute(
"x",
"0");
370 svgWriter.writeAttribute(
"dy", em);
374 case QXmlStreamReader::EndElement:
376 if (htmlReader.name() ==
"br")
break;
377 if (elementName ==
"p" || spanLikes.contains(elementName) || elementName ==
"body") {
378 debugFlake <<
"\tEndElement" << htmlReader.name() <<
"(" << elementName <<
")";
379 svgWriter.writeEndElement();
383 case QXmlStreamReader::Characters:
385 if (elementName ==
"style") {
386 *styles = htmlReader.text().toString();
391 debugFlake <<
"\tCharacters:" << htmlReader.text();
392 svgWriter.writeCharacters(htmlReader.text().toString());
402 if (htmlReader.hasError()) {
403 d->errors << htmlReader.errorString();
406 if (svgWriter.hasError()) {
407 d->errors << i18n(
"Unknown error writing SVG text element");
411 *svgText = QString::fromUtf8(svgBuffer.data());
440 QTextCharFormat mostCommonFormat;
442 QSet<int> propertyIds;
447 Q_FOREACH (
const QTextFormat &format, allFormats) {
448 const QMap<int, QVariant> formatProperties = format.properties();
449 Q_FOREACH (
int id, formatProperties.keys()) {
450 propertyIds.insert(
id);
460 Q_FOREACH (
const QTextFormat &format, allFormats) {
461 for (
auto it = propertyIds.begin(); it != propertyIds.end();) {
462 if (!format.hasProperty(*it)) {
463 it = propertyIds.erase(it);
468 if (propertyIds.isEmpty())
break;
471 if (!propertyIds.isEmpty()) {
472 QMap<int, QList<std::pair<QVariant, int>>> propertyFrequency;
477 Q_FOREACH (
const QTextFormat &format, allFormats) {
478 const QMap<int, QVariant> formatProperties = format.properties();
480 Q_FOREACH (
int id, propertyIds) {
483 const QVariant formatPropValue = formatProperties.value(
id);
486 auto it = std::find_if(valueFrequencies.begin(), valueFrequencies.end(),
487 [formatPropValue](
const std::pair<QVariant, int> &element) { return element.first == formatPropValue; });
489 if (it != valueFrequencies.end()) {
494 valueFrequencies.push_back({formatPropValue, 1});
502 for (
auto it = propertyFrequency.constBegin(); it != propertyFrequency.constEnd(); ++it) {
503 const int id = it.key();
509 for (
const auto& [propValue, valFrequency] : allValues) {
510 if (valFrequency > maxCount) {
511 maxCount = valFrequency;
512 maxValue = propValue;
517 mostCommonFormat.setProperty(
id, maxValue);
522 return mostCommonFormat;
583 svgBuffer.open(QIODevice::WriteOnly);
585 QXmlStreamWriter svgWriter(&svgBuffer);
588 svgWriter.setAutoFormatting(
false);
591 qreal maxParagraphWidth = 0.0;
592 QTextCharFormat mostCommonCharFormat;
593 QTextBlockFormat mostCommonBlockFormat;
597 LineInfo(QTextBlock _block,
int _numSkippedLines)
598 : block(_block), numSkippedLines(_numSkippedLines)
602 int numSkippedLines = 0;
626 QTextBlock block = doc->begin();
631 int numSequentialEmptyLines = 0;
633 bool hasExplicitTextWidth =
false;
636 if (std::optional<double> inlineSize =
getInlineSize(doc->rootFrame()->frameFormat())) {
637 if (*inlineSize > 0.0) {
638 hasExplicitTextWidth =
true;
639 maxParagraphWidth = *inlineSize;
644 while (block.isValid()) {
646 lineInfoList.append(LineInfo(block, numSequentialEmptyLines));
647 numSequentialEmptyLines = 0;
649 if (!hasExplicitTextWidth) {
650 maxParagraphWidth = qMax(maxParagraphWidth,
calcLineWidth(block));
653 allBlockFormats.append(block.blockFormat());
654 Q_FOREACH (
const QTextLayout::FormatRange &range, block.textFormats()) {
655 QTextFormat format = range.format;
656 allCharFormats.append(format);
659 numSequentialEmptyLines++;
662 block = block.next();
671 QTextBlock block = doc->begin();
673 svgWriter.writeStartElement(
"text");
678 if (block.textDirection() == Qt::RightToLeft) {
679 svgWriter.writeAttribute(
"direction",
"rtl");
684 QString commonTextStyle =
style(mostCommonCharFormat,
685 mostCommonBlockFormat,
689 if (!commonTextStyle.isEmpty()) {
690 commonTextStyle +=
"; ";
692 commonTextStyle +=
"white-space: pre";
694 commonTextStyle +=
"-wrap;inline-size:";
695 commonTextStyle += QString::number(maxParagraphWidth);
698 if (!commonTextStyle.isEmpty()) {
699 svgWriter.writeAttribute(
"style", commonTextStyle);
704 int prevBlockRelativeLineSpacing = mostCommonBlockFormat.lineHeight();
705 int prevBlockLineType = mostCommonBlockFormat.lineHeightType();
706 qreal prevBlockAscent = 0.0;
707 qreal prevBlockDescent= 0.0;
709 Q_FOREACH (
const LineInfo &info, lineInfoList) {
710 QTextBlock block = info.block;
712 const QTextBlockFormat blockFormatDiff =
formatDifference(block.blockFormat(), mostCommonBlockFormat).toBlockFormat();
713 QTextCharFormat blockCharFormatDiff = QTextCharFormat();
715 if (formats.size()==1) {
716 blockCharFormatDiff =
formatDifference(formats.at(0).format, mostCommonCharFormat).toCharFormat();
720 blockCharFormatDiff.clearProperty(QTextBlockFormat::BlockAlignment);
724 const QTextLayout *layout = block.layout();
725 const QTextLine line = layout->lineAt(0);
726 if (!line.isValid()) {
734 svgWriter.writeStartElement(
"tspan");
736 const QString text = block.text();
739 switch (block.textDirection()) {
740 case Qt::LeftToRight:
741 isRightToLeft =
false;
743 case Qt::RightToLeft:
744 isRightToLeft =
true;
746 case Qt::LayoutDirectionAuto:
755 svgWriter.writeAttribute(
"direction",
"rtl");
756 svgWriter.writeAttribute(
"unicode-bidi",
"embed");
760 const QString blockStyleString =
style(blockCharFormatDiff,
764 if (!blockStyleString.isEmpty()) {
765 svgWriter.writeAttribute(
"style", blockStyleString);
775 Qt::Alignment blockAlignment = block.blockFormat().alignment();
777 if (blockAlignment & Qt::AlignLeft) {
778 blockAlignment &= ~Qt::AlignLeft;
779 blockAlignment |= Qt::AlignRight;
780 }
else if (blockAlignment & Qt::AlignRight) {
781 blockAlignment &= ~Qt::AlignRight;
782 blockAlignment |= Qt::AlignLeft;
786 if (blockAlignment & Qt::AlignHCenter) {
788 }
else if (blockAlignment & Qt::AlignRight) {
791 svgWriter.writeAttribute(
"x",
"0");
798 (prevBlockAscent + prevBlockDescent) * qreal(prevBlockRelativeLineSpacing) / 100.0;
800 const qreal currentLineSpacing = (info.numSkippedLines + 1) * lineHeightPt;
804 prevBlockRelativeLineSpacing =
805 blockFormatDiff.hasProperty(QTextFormat::LineHeight) ?
806 blockFormatDiff.lineHeight() :
807 mostCommonBlockFormat.lineHeight();
810 blockFormatDiff.hasProperty(QTextFormat::LineHeightType) ?
811 blockFormatDiff.lineHeightType() :
812 mostCommonBlockFormat.lineHeightType();
814 if (prevBlockLineType == QTextBlockFormat::SingleHeight) {
816 prevBlockRelativeLineSpacing = 100;
823 if (formats.size()>1) {
826 for (
int f=0; f<formats.size(); f++) {
828 for (
int c = 0; c<formats.at(f).
length; c++) {
829 chunk.append(text.at(formats.at(f).start+c));
832 charFormats.append(formats.at(f).format);
835 for (
int c = 0; c<texts.size(); c++) {
836 QTextCharFormat diff =
formatDifference(charFormats.at(c), mostCommonCharFormat).toCharFormat();
837 const QString subStyle =
style(diff, QTextBlockFormat(), mostCommonCharFormat);
838 if (!subStyle.isEmpty()) {
839 svgWriter.writeStartElement(
"tspan");
840 svgWriter.writeAttribute(
"style", subStyle);
841 svgWriter.writeCharacters(texts.at(c));
842 svgWriter.writeEndElement();
844 svgWriter.writeCharacters(texts.at(c));
849 svgWriter.writeCharacters(text);
855 svgWriter.writeCharacters(QLatin1String(
"\n"));
857 svgWriter.writeEndElement();
859 svgWriter.writeEndElement();
861 if (svgWriter.hasError()) {
862 d->errors << i18n(
"Unknown error writing SVG text element");
865 *svgText = QString::fromUtf8(svgBuffer.data()).trimmed();
870 QTextCharFormat &charFormat,
871 QTextBlockFormat &blockFormat,
877 QString presentationAttributes;
878 for (
int a = 0; a < elementAttributes.size(); a++) {
879 if (elementAttributes.at(a).name() !=
"style") {
880 presentationAttributes
881 .append(elementAttributes.at(a).name().toString())
883 .append(elementAttributes.at(a).value().toString())
888 if (presentationAttributes.endsWith(
";")) {
889 presentationAttributes.chop(1);
892 if (elementAttributes.hasAttribute(
"style")) {
893 styleString = elementAttributes.value(
"style").toString();
894 if (styleString.endsWith(
";")) {
899 if (!styleString.isEmpty() || !presentationAttributes.isEmpty()) {
901 styleString.append(
";")
902 .append(presentationAttributes);
907 charFormat = formats.at(0).toCharFormat();
908 blockFormat = formats.at(1).toBlockFormat();
914 QXmlStreamReader svgReader(svgText.trimmed());
916 QTextCursor cursor(doc);
918 struct BlockFormatRecord {
919 BlockFormatRecord() {}
920 BlockFormatRecord(QTextBlockFormat _blockFormat,
921 QTextCharFormat _charFormat)
922 : blockFormat(_blockFormat),
923 charFormat(_charFormat)
926 QTextBlockFormat blockFormat;
927 QTextCharFormat charFormat;
931 formatStack.push(BlockFormatRecord(QTextBlockFormat(), QTextCharFormat()));
932 cursor.setCharFormat(formatStack.top().charFormat);
933 cursor.setBlockFormat(formatStack.top().blockFormat);
935 qreal currBlockAbsoluteLineOffset = 0.0;
936 int prevBlockCursorPosition = -1;
937 Qt::Alignment prevBlockAlignment = Qt::AlignLeft;
938 bool prevTspanHasTrailingLF =
false;
939 qreal prevLineDescent = 0.0;
940 qreal prevLineAscent = 0.0;
942 boost::optional<qreal> previousBlockAbsoluteXOffset =
943 boost::optional<qreal>(
false, qreal());
945 std::optional<ExtraStyles> docExtraStyles;
947 while (!svgReader.atEnd()) {
948 QXmlStreamReader::TokenType token = svgReader.readNext();
950 case QXmlStreamReader::StartElement:
952 prevTspanHasTrailingLF =
false;
954 bool newBlock =
false;
955 QTextBlockFormat newBlockFormat;
956 QTextCharFormat newCharFormat;
957 qreal absoluteLineOffset = 1.0;
960 if (!formatStack.empty()) {
961 newBlockFormat = formatStack.top().blockFormat;
962 newCharFormat = formatStack.top().charFormat;
967 const QXmlStreamAttributes elementAttributes = svgReader.attributes();
970 if (!docExtraStyles && svgReader.name() == QLatin1String(
"text")) {
971 if (extraStyles.inlineSize > 0.0) {
979 docExtraStyles = extraStyles;
987 boost::optional<qreal> blockAbsoluteXOffset =
988 boost::make_optional(
false, qreal());
990 if (elementAttributes.hasAttribute(
"x")) {
991 QString xString = elementAttributes.value(
"x").toString();
992 if (xString.contains(
"pt")) {
993 xString = xString.remove(
"pt").trimmed();
1000 Qt::Alignment thisBlockAlignment = Qt::AlignLeft;
1001 if (newBlockFormat.hasProperty(QTextBlockFormat::BlockAlignment)) {
1002 thisBlockAlignment = newBlockFormat.alignment();
1003 }
else if (!formatStack.empty()) {
1004 thisBlockAlignment = formatStack.top().blockFormat.alignment();
1007 const auto isSameXOffset = [&]() {
1008 return previousBlockAbsoluteXOffset && blockAbsoluteXOffset
1009 &&
qFuzzyCompare(*previousBlockAbsoluteXOffset, *blockAbsoluteXOffset);
1011 if ((isSameXOffset() || thisBlockAlignment != prevBlockAlignment) && svgReader.name() !=
"text"
1012 && elementAttributes.hasAttribute(
"dy")) {
1014 QString dyString = elementAttributes.value(
"dy").toString();
1015 if (dyString.contains(
"pt")) {
1016 dyString = dyString.remove(
"pt").trimmed();
1022 newBlock = absoluteLineOffset > 0;
1025 if (elementAttributes.hasAttribute(
"x")) {
1026 previousBlockAbsoluteXOffset = blockAbsoluteXOffset;
1028 prevBlockAlignment = thisBlockAlignment;
1032 doc->setTextWidth(100);
1033 doc->setTextWidth(-1);
1035 if (newBlock && absoluteLineOffset > 0) {
1039 QTextLine line = cursor.block().layout()->lineAt(0);
1041 if (prevBlockCursorPosition >= 0) {
1043 prevBlockCursorPosition, currBlockAbsoluteLineOffset);
1046 prevBlockCursorPosition = cursor.position();
1047 prevLineAscent = line.ascent();
1048 prevLineDescent = line.descent();
1049 currBlockAbsoluteLineOffset = absoluteLineOffset;
1051 cursor.insertBlock();
1052 cursor.setCharFormat(formatStack.top().charFormat);
1053 cursor.setBlockFormat(formatStack.top().blockFormat);
1056 cursor.mergeCharFormat(newCharFormat);
1057 cursor.mergeBlockFormat(newBlockFormat);
1059 formatStack.push(BlockFormatRecord(cursor.blockFormat(), cursor.charFormat()));
1063 case QXmlStreamReader::EndElement:
1065 if (svgReader.name() !=
"text") {
1069 cursor.setCharFormat(formatStack.top().charFormat);
1081 && prevTspanHasTrailingLF) {
1082 cursor.setBlockFormat(formatStack.top().blockFormat);
1084 prevTspanHasTrailingLF =
false;
1088 case QXmlStreamReader::Characters:
1090 cursor.insertText(svgReader.text().toString());
1091 prevTspanHasTrailingLF = svgReader.text().endsWith(
'\n');
1099 if (prevBlockCursorPosition >= 0) {
1100 QTextLine line = cursor.block().layout()->lineAt(0);
1102 prevBlockCursorPosition, currBlockAbsoluteLineOffset);
1106 if (!docExtraStyles) {
1109 QTextFrameFormat f = doc->rootFrame()->frameFormat();
1112 doc->rootFrame()->setFrameFormat(f);
1115 if (svgReader.hasError()) {
1116 d->errors << svgReader.errorString();
1119 doc->setModified(
false);
1179 QTextBlockFormat blockFormat,
1180 QTextCharFormat mostCommon,
1181 const bool includeLineHeight)
1184 for(
int i=0; i<format.properties().size(); i++) {
1186 int propertyId = format.properties().keys().at(i);
1188 if (propertyId == QTextCharFormat::FontFamily) {
1189 const QString fontFamily = format.properties()[propertyId].toString();
1190 c.append(
"font-family").append(
":").append(fontFamily);
1192 if (propertyId == QTextCharFormat::FontPointSize ||
1193 propertyId == QTextCharFormat::FontPixelSize) {
1197 c.append(
"font-size").append(
":")
1198 .append(format.properties()[propertyId].toString());
1200 if (propertyId == QTextCharFormat::FontWeight) {
1203 int convertedWeight = 400;
1205 switch (format.properties()[propertyId].toInt()) {
1206 case QFont::Weight::Thin:
1207 convertedWeight = 100;
1209 case QFont::Weight::ExtraLight:
1210 convertedWeight = 200;
1212 case QFont::Weight::Light:
1213 convertedWeight = 300;
1215 case QFont::Weight::Normal:
1216 convertedWeight = 400;
1218 case QFont::Weight::Medium:
1219 convertedWeight = 500;
1221 case QFont::Weight::DemiBold:
1222 convertedWeight = 600;
1224 case QFont::Weight::Bold:
1225 convertedWeight = 700;
1227 case QFont::Weight::ExtraBold:
1228 convertedWeight = 800;
1230 case QFont::Weight::Black:
1231 convertedWeight = 900;
1234 warnFile <<
"WARNING: Invalid QFont::Weight value supplied to KoSvgTextShapeMarkupConverter::style.";
1238 c.append(
"font-weight").append(
":")
1239 .append(QString::number(convertedWeight));
1241 if (propertyId == QTextCharFormat::FontItalic) {
1242 QString val =
"italic";
1243 if (!format.fontItalic()) {
1246 c.append(
"font-style").append(
":")
1250 if (propertyId == QTextCharFormat::FontCapitalization) {
1251 if (format.fontCapitalization() == QFont::SmallCaps){
1252 c.append(
"font-variant").append(
":")
1253 .append(
"small-caps");
1254 }
else if (format.fontCapitalization() == QFont::AllUppercase) {
1255 c.append(
"text-transform").append(
":")
1256 .append(
"uppercase");
1257 }
else if (format.fontCapitalization() == QFont::AllLowercase) {
1258 c.append(
"text-transform").append(
":")
1259 .append(
"lowercase");
1260 }
else if (format.fontCapitalization() == QFont::Capitalize) {
1261 c.append(
"text-transform").append(
":")
1262 .append(
"capitalize");
1266 if (propertyId == QTextCharFormat::FontStretch) {
1267 QString valueString = QString::number(format.fontStretch(), 10);
1268 if (format.fontStretch() == QFont::ExtraCondensed) {
1269 valueString =
"extra-condensed";
1270 }
else if (format.fontStretch() == QFont::SemiCondensed) {
1271 valueString =
"semi-condensed";
1272 }
else if (format.fontStretch() == QFont::Condensed) {
1273 valueString =
"condensed";
1274 }
else if (format.fontStretch() == QFont::AnyStretch) {
1275 valueString =
"normal";
1276 }
else if (format.fontStretch() == QFont::Expanded) {
1277 valueString =
"expanded";
1278 }
else if (format.fontStretch() == QFont::SemiExpanded) {
1279 valueString =
"semi-expanded";
1280 }
else if (format.fontStretch() == QFont::ExtraExpanded) {
1281 valueString =
"extra-expanded";
1282 }
else if (format.fontStretch() == QFont::UltraExpanded) {
1283 valueString =
"ultra-expanded";
1285 c.append(
"font-stretch").append(
":")
1286 .append(valueString);
1288 if (propertyId == QTextCharFormat::FontKerning) {
1290 if (format.fontKerning()) {
1295 c.append(
"kerning").append(
":")
1298 if (propertyId == QTextCharFormat::FontWordSpacing) {
1299 c.append(
"word-spacing").append(
":")
1300 .append(QString::number(format.fontWordSpacing()));
1302 if (propertyId == QTextCharFormat::FontLetterSpacing) {
1304 if (format.fontLetterSpacingType()==QFont::AbsoluteSpacing) {
1305 val = QString::number(format.fontLetterSpacing());
1307 val = QString::number(((format.fontLetterSpacing()/100)*format.fontPointSize()));
1309 c.append(
"letter-spacing").append(
":")
1312 if (propertyId == QTextCharFormat::TextOutline) {
1313 if (format.textOutline().color() != mostCommon.textOutline().color()) {
1314 c.append(
"stroke").append(
":")
1315 .append(format.textOutline().color().name());
1319 if (format.textOutline().width() != mostCommon.textOutline().width()) {
1320 c.append(
"stroke-width").append(
":")
1321 .append(QString::number(format.textOutline().width()));
1326 if (propertyId == QTextCharFormat::TextVerticalAlignment) {
1327 QString val =
"baseline";
1328 if (format.verticalAlignment() == QTextCharFormat::AlignSubScript) {
1329 val = QLatin1String(
"sub");
1331 else if (format.verticalAlignment() == QTextCharFormat::AlignSuperScript) {
1332 val = QLatin1String(
"super");
1334 c.append(
"baseline-shift").append(
":").append(val);
1337 if (propertyId == QTextCharFormat::ForegroundBrush) {
1338 QColor::NameFormat colorFormat;
1340 if (format.foreground().color().alphaF() < 1.0) {
1341 colorFormat = QColor::HexArgb;
1343 colorFormat = QColor::HexRgb;
1346 c.append(
"fill").append(
":")
1347 .append(format.foreground().color().name(colorFormat));
1363 if (blockFormat.hasProperty(QTextBlockFormat::BlockAlignment)) {
1369 if (blockFormat.alignment()==Qt::AlignRight) {
1371 }
else if (blockFormat.alignment()==Qt::AlignCenter) {
1376 c.append(
"text-anchor").append(
":")
1383 if (includeLineHeight && blockFormat.hasProperty(QTextBlockFormat::LineHeight)) {
1385 if (blockFormat.lineHeightType() == QTextBlockFormat::ProportionalHeight) {
1386 h = blockFormat.lineHeight() / 100.0;
1387 }
else if (blockFormat.lineHeightType() == QTextBlockFormat::SingleHeight) {
1390 QString c =
"line-height:";
1392 c += QString::number(blockFormat.lineHeight() / 100.0);
1399 return style.join(
"; ");
1403 QTextCharFormat currentCharFormat,
1404 QTextBlockFormat currentBlockFormat,
1407 Q_UNUSED(currentBlockFormat);
1410 QTextCharFormat charFormat;
1411 charFormat.setTextOutline(currentCharFormat.textOutline());
1412 QTextBlockFormat blockFormat;
1416 for (
int i=0; i<styles.size(); i++) {
1417 if (!styles.at(i).isEmpty()){
1421 if (
style.size() < 2) {
1425 QString
property =
style.at(0).trimmed();
1428 if (property ==
"font-family") {
1429 charFormat.setFontFamily(
value);
1432 if (property ==
"font-size") {
1434 charFormat.setFontPointSize(val);
1437 if (property ==
"font-variant") {
1438 if (
value==
"small-caps") {
1439 charFormat.setFontCapitalization(QFont::SmallCaps);
1441 charFormat.setFontCapitalization(QFont::MixedCase);
1445 if (property ==
"font-style") {
1447 charFormat.setFontItalic(
true);
1449 charFormat.setFontItalic(
false);
1453 if (property ==
"font-stretch") {
1454 if (
value ==
"ultra-condensed") {
1455 charFormat.setFontStretch(QFont::UltraCondensed);
1456 }
else if (
value ==
"condensed") {
1457 charFormat.setFontStretch(QFont::Condensed);
1458 }
else if (
value ==
"semi-condensed") {
1459 charFormat.setFontStretch(QFont::SemiCondensed);
1460 }
else if (
value ==
"normal") {
1461 charFormat.setFontStretch(100);
1462 }
else if (
value ==
"semi-expanded") {
1463 charFormat.setFontStretch(QFont::SemiExpanded);
1464 }
else if (
value ==
"expanded") {
1465 charFormat.setFontStretch(QFont::Expanded);
1466 }
else if (
value ==
"extra-expanded") {
1467 charFormat.setFontStretch(QFont::ExtraExpanded);
1468 }
else if (
value ==
"ultra-expanded") {
1469 charFormat.setFontStretch(QFont::UltraExpanded);
1471 charFormat.setFontStretch(
value.toInt());
1475 if (property ==
"font-weight") {
1478 int convertedWeight = QFont::Weight::Normal;
1480 switch (
value.toInt()) {
1482 convertedWeight = QFont::Weight::Thin;
1485 convertedWeight = QFont::Weight::ExtraLight;
1488 convertedWeight = QFont::Weight::Light;
1491 convertedWeight = QFont::Weight::Normal;
1494 convertedWeight = QFont::Weight::Medium;
1497 convertedWeight = QFont::Weight::DemiBold;
1500 convertedWeight = QFont::Weight::Bold;
1503 convertedWeight = QFont::Weight::ExtraBold;
1506 convertedWeight = QFont::Weight::Black;
1509 warnFile <<
"WARNING: Invalid weight value supplied to KoSvgTextShapeMarkupConverter::stylesFromString.";
1513 charFormat.setFontWeight(convertedWeight);
1516 if (property ==
"text-decoration") {
1517 charFormat.setFontUnderline(
false);
1518 charFormat.setFontOverline(
false);
1519 charFormat.setFontStrikeOut(
false);
1521 if (values.contains(
"line-through")) {
1522 charFormat.setFontStrikeOut(
true);
1524 if (values.contains(
"overline")) {
1525 charFormat.setFontOverline(
true);
1527 if(values.contains(
"underline")){
1528 charFormat.setFontUnderline(
true);
1532 if (property ==
"text-transform") {
1533 if (
value ==
"uppercase") {
1534 charFormat.setFontCapitalization(QFont::AllUppercase);
1535 }
else if (
value ==
"lowercase") {
1536 charFormat.setFontCapitalization(QFont::AllLowercase);
1537 }
else if (
value ==
"capitalize") {
1538 charFormat.setFontCapitalization(QFont::Capitalize);
1540 charFormat.setFontCapitalization(QFont::MixedCase);
1544 if (property ==
"letter-spacing") {
1546 charFormat.setFontLetterSpacingType(QFont::AbsoluteSpacing);
1547 charFormat.setFontLetterSpacing(val);
1550 if (property ==
"word-spacing") {
1552 charFormat.setFontWordSpacing(val);
1555 if (property ==
"kerning") {
1556 if (
value ==
"auto") {
1557 charFormat.setFontKerning(
true);
1560 charFormat.setFontKerning(
false);
1561 charFormat.setFontLetterSpacingType(QFont::AbsoluteSpacing);
1562 charFormat.setFontLetterSpacing(charFormat.fontLetterSpacing() + val);
1566 if (property ==
"stroke") {
1567 QPen pen = charFormat.textOutline();
1569 color.setNamedColor(
value);
1570 pen.setColor(color);
1571 charFormat.setTextOutline(pen);
1574 if (property ==
"stroke-width") {
1575 QPen pen = charFormat.textOutline();
1576 pen.setWidth(
value.toInt());
1577 charFormat.setTextOutline(pen);
1580 if (property ==
"fill") {
1582 color.setNamedColor(
value);
1585 if (!color.isValid()) {
1590 qreal currentAlpha = charFormat.foreground().color().alphaF();
1593 if (currentAlpha < 1.0) {
1595 if (color.alphaF() < 1.0) {
1596 color.setAlphaF(currentAlpha);
1600 charFormat.setForeground(color);
1603 if (property ==
"fill-opacity") {
1604 QColor color = charFormat.foreground().color();
1611 alpha = color.alphaF();
1613 color.setAlphaF(alpha);
1614 charFormat.setForeground(color);
1617 if (property ==
"text-anchor") {
1618 if (
value ==
"end") {
1619 blockFormat.setAlignment(Qt::AlignRight);
1620 }
else if (
value ==
"middle") {
1621 blockFormat.setAlignment(Qt::AlignCenter);
1623 blockFormat.setAlignment(Qt::AlignLeft);
1627 if (property ==
"baseline-shift") {
1628 if (
value ==
"super") {
1629 charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
1630 }
else if (
value ==
"sub") {
1631 charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript);
1633 charFormat.setVerticalAlignment(QTextCharFormat::AlignNormal);
1637 if (property ==
"line-height") {
1638 double lineHeightPercent = -1.0;
1640 if (
value.endsWith(
'%')) {
1644 lineHeightPercent =
value.left(
value.length() - 1).toDouble(&ok);
1646 lineHeightPercent = -1.0;
1648 }
else if(
const double unitless =
value.toDouble(&ok); ok) {
1649 lineHeightPercent = unitless * 100.0;
1650 }
else if (
value == QLatin1String(
"normal")) {
1651 lineHeightPercent = -1.0;
1652 blockFormat.setLineHeight(1, QTextBlockFormat::SingleHeight);
1654 if (lineHeightPercent >= 0) {
1655 blockFormat.setLineHeight(lineHeightPercent, QTextBlockFormat::ProportionalHeight);
1659 if (property ==
"inline-size") {
1666 if (property ==
"white-space") {
1667 if (
value == QLatin1String(
"pre")) {
1669 }
else if (
value == QLatin1String(
"pre-wrap")) {
1678 formats.append(charFormat);
1679 formats.append(blockFormat);
void parseTextAttributes(const QXmlStreamAttributes &elementAttributes, QTextCharFormat &charFormat, QTextBlockFormat &blockFormat, KoSvgTextShapeMarkupConverter::ExtraStyles &extraStyles)
void postCorrectBlockHeight(QTextDocument *doc, qreal currLineAscent, qreal prevLineAscent, qreal prevLineDescent, int prevBlockCursorPosition, qreal currentBlockAbsoluteLineOffset)
QString style(QTextCharFormat format, QTextBlockFormat blockFormat, QTextCharFormat mostCommon=QTextCharFormat(), bool includeLineHeight=false)
style creates a style string based on the blockformat and the format.
static void setWrappingMode(QTextFrameFormat *frameFormat, WrappingMode wrappingMode)
Set the Wrapping Mode on a frame format to be applied to the rootFrame of a QTextDocument.
bool convertFromHtml(const QString &htmlText, QString *svgText, QString *styles)
convertFromHtml converted Qt rich text html (and no other: https://doc.qt.io/qt-5/richtext-html-subse...
static QVector< QTextFormat > stylesFromString(QStringList styles, QTextCharFormat currentCharFormat, QTextBlockFormat currentBlockFormat, ExtraStyles &extraStyles)
stylesFromString returns a qvector with two textformats: at 0 is the QTextCharFormat at 1 is the QTex...
bool convertFromSvg(const QString &svgText, const QString &stylesText, const QRectF &boundsInPixels, qreal pixelsPerInch)
upload the svg representation of text into the shape
static void setInlineSize(QTextFrameFormat *frameFormat, double inlineSize)
Set or unset the inline-size on a frameFormat to be applied to the rootFrame of a QTextDocument.