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;
222 bool firstElement =
true;
224 while (!htmlReader.atEnd()) {
225 QXmlStreamReader::TokenType token = htmlReader.readNext();
226 QLatin1String elName = firstElement? QLatin1String(
"text"): QLatin1String(
"tspan");
228 case QXmlStreamReader::StartElement:
231 if (htmlReader.name() ==
"br") {
233 svgWriter.writeEndElement();
234#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
235 elementName = QStringRef(&
p);
237 elementName = QStringView(
p);
240 appendStyle = previousStyleString;
243 elementName = htmlReader.name();
247 if (elementName ==
"body") {
248 debugFlake <<
"\tstart Element" << elementName;
249 svgWriter.writeStartElement(elName);
250 firstElement =
false;
251 appendStyle = QString();
253 else if (elementName ==
"p") {
255 debugFlake <<
"\t\tstart Element" << elementName;
256 svgWriter.writeStartElement(elName);
257 firstElement =
false;
261 appendStyle = QString();
265 else if (elementName ==
"span") {
266 debugFlake <<
"\tstart Element" << elementName;
267 svgWriter.writeStartElement(elName);
268 firstElement =
false;
269 appendStyle = QString();
271 else if (elementName ==
"b" || elementName ==
"strong") {
272 debugFlake <<
"\tstart Element" << elementName;
273 svgWriter.writeStartElement(elName);
274 firstElement =
false;
275 appendStyle =
"font-weight:700;";
277 else if (elementName ==
"i" || elementName ==
"em") {
278 debugFlake <<
"\tstart Element" << elementName;
279 svgWriter.writeStartElement(elName);
280 firstElement =
false;
281 appendStyle =
"font-style:italic;";
283 else if (elementName ==
"u") {
284 debugFlake <<
"\tstart Element" << elementName;
285 svgWriter.writeStartElement(elName);
286 firstElement =
false;
287 appendStyle =
"text-decoration:underline";
289 else if (elementName ==
"font") {
290 debugFlake <<
"\tstart Element" << elementName;
291 svgWriter.writeStartElement(elName);
292 firstElement =
false;
293 appendStyle = QString();
294 if (htmlReader.attributes().hasAttribute(
"color")) {
295 svgWriter.writeAttribute(
"fill", htmlReader.attributes().value(
"color").toString());
298 else if (elementName ==
"pre") {
299 debugFlake <<
"\tstart Element" << elementName;
300 svgWriter.writeStartElement(elName);
301 firstElement =
false;
302 appendStyle =
"white-space:pre";
305 QXmlStreamAttributes attributes = htmlReader.attributes();
308 if (attributes.hasAttribute(
"align")) {
309 textAlign = attributes.value(
"align").toString();
312 if (attributes.hasAttribute(
"style")) {
313 QString filteredStyles;
314 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(
" ");
315 QStringList styles = attributes.value(
"style").toString().split(
";");
316 for(
int i=0; i<styles.size(); i++) {
319 if (svgStyles.contains(QString(
style.at(0)).trimmed())) {
320 filteredStyles.append(styles.at(i)+
";");
323 if (QString(
style.at(0)).trimmed() ==
"color") {
324 filteredStyles.append(
" fill:"+
style.at(1)+
";");
327 if (QString(
style.at(0)).trimmed() ==
"text-align") {
328 textAlign = QString(
style.at(1)).trimmed();
331 if (QString(
style.at(0)).trimmed() ==
"line-height"){
332 if (
style.at(1).contains(
"%")) {
333 double percentage = QString(
style.at(1)).remove(
"%").toDouble();
334 em = QString::number(percentage/100.0)+
"em";
335 }
else if(
style.at(1).contains(
"em")) {
337 }
else if(
style.at(1).contains(
"px")) {
340 if (elementName ==
"body") {
346 if (textAlign ==
"center") {
347 filteredStyles.append(
" text-anchor:middle;");
348 }
else if (textAlign ==
"right") {
349 filteredStyles.append(
" text-anchor:end;");
350 }
else if (textAlign ==
"left"){
351 filteredStyles.append(
" text-anchor:start;");
354 filteredStyles.append(appendStyle);
356 if (!filteredStyles.isEmpty()) {
357 svgWriter.writeAttribute(
"style", filteredStyles);
358 previousStyleString = filteredStyles;
363 if (newLine && lineCount > 1) {
364 debugFlake <<
"\t\tAdvancing to the next line";
365 svgWriter.writeAttribute(
"x",
"0");
366 svgWriter.writeAttribute(
"dy", em);
370 case QXmlStreamReader::EndElement:
372 if (htmlReader.name() ==
"br")
break;
373 if (elementName ==
"p" || elementName ==
"span" || elementName ==
"body") {
374 debugFlake <<
"\tEndElement" << htmlReader.name() <<
"(" << elementName <<
")";
375 svgWriter.writeEndElement();
379 case QXmlStreamReader::Characters:
381 if (elementName ==
"style") {
382 *styles = htmlReader.text().toString();
387 debugFlake <<
"\tCharacters:" << htmlReader.text();
388 svgWriter.writeCharacters(htmlReader.text().toString());
398 if (htmlReader.hasError()) {
399 d->errors << htmlReader.errorString();
402 if (svgWriter.hasError()) {
403 d->errors << i18n(
"Unknown error writing SVG text element");
407 *svgText = QString::fromUtf8(svgBuffer.data());
436 QTextCharFormat mostCommonFormat;
438 QSet<int> propertyIds;
443 Q_FOREACH (
const QTextFormat &format, allFormats) {
444 const QMap<int, QVariant> formatProperties = format.properties();
445 Q_FOREACH (
int id, formatProperties.keys()) {
446 propertyIds.insert(
id);
456 Q_FOREACH (
const QTextFormat &format, allFormats) {
457 for (
auto it = propertyIds.begin(); it != propertyIds.end();) {
458 if (!format.hasProperty(*it)) {
459 it = propertyIds.erase(it);
464 if (propertyIds.isEmpty())
break;
467 if (!propertyIds.isEmpty()) {
468 QMap<int, QList<std::pair<QVariant, int>>> propertyFrequency;
473 Q_FOREACH (
const QTextFormat &format, allFormats) {
474 const QMap<int, QVariant> formatProperties = format.properties();
476 Q_FOREACH (
int id, propertyIds) {
479 const QVariant formatPropValue = formatProperties.value(
id);
482 auto it = std::find_if(valueFrequencies.begin(), valueFrequencies.end(),
483 [formatPropValue](
const std::pair<QVariant, int> &element) { return element.first == formatPropValue; });
485 if (it != valueFrequencies.end()) {
490 valueFrequencies.push_back({formatPropValue, 1});
498 for (
auto it = propertyFrequency.constBegin(); it != propertyFrequency.constEnd(); ++it) {
499 const int id = it.key();
505 for (
const auto& [propValue, valFrequency] : allValues) {
506 if (valFrequency > maxCount) {
507 maxCount = valFrequency;
508 maxValue = propValue;
513 mostCommonFormat.setProperty(
id, maxValue);
518 return mostCommonFormat;
579 svgBuffer.open(QIODevice::WriteOnly);
581 QXmlStreamWriter svgWriter(&svgBuffer);
584 svgWriter.setAutoFormatting(
false);
587 qreal maxParagraphWidth = 0.0;
588 QTextCharFormat mostCommonCharFormat;
589 QTextBlockFormat mostCommonBlockFormat;
593 LineInfo(QTextBlock _block,
int _numSkippedLines)
594 : block(_block), numSkippedLines(_numSkippedLines)
598 int numSkippedLines = 0;
622 QTextBlock block = doc->begin();
627 int numSequentialEmptyLines = 0;
629 bool hasExplicitTextWidth =
false;
632 if (std::optional<double> inlineSize =
getInlineSize(doc->rootFrame()->frameFormat())) {
633 if (*inlineSize > 0.0) {
634 hasExplicitTextWidth =
true;
635 maxParagraphWidth = *inlineSize;
640 while (block.isValid()) {
642 lineInfoList.append(LineInfo(block, numSequentialEmptyLines));
643 numSequentialEmptyLines = 0;
645 if (!hasExplicitTextWidth) {
646 maxParagraphWidth = qMax(maxParagraphWidth,
calcLineWidth(block));
649 allBlockFormats.append(block.blockFormat());
650 Q_FOREACH (
const QTextLayout::FormatRange &range, block.textFormats()) {
651 QTextFormat format = range.format;
652 allCharFormats.append(format);
655 numSequentialEmptyLines++;
658 block = block.next();
667 QTextBlock block = doc->begin();
669 svgWriter.writeStartElement(
"text");
674 if (block.textDirection() == Qt::RightToLeft) {
675 svgWriter.writeAttribute(
"direction",
"rtl");
680 QString commonTextStyle =
style(mostCommonCharFormat,
681 mostCommonBlockFormat,
685 if (!commonTextStyle.isEmpty()) {
686 commonTextStyle +=
"; ";
688 commonTextStyle +=
"white-space: pre";
690 commonTextStyle +=
"-wrap;inline-size:";
691 commonTextStyle += QString::number(maxParagraphWidth);
694 if (!commonTextStyle.isEmpty()) {
695 svgWriter.writeAttribute(
"style", commonTextStyle);
700 int prevBlockRelativeLineSpacing = mostCommonBlockFormat.lineHeight();
701 int prevBlockLineType = mostCommonBlockFormat.lineHeightType();
702 qreal prevBlockAscent = 0.0;
703 qreal prevBlockDescent= 0.0;
705 Q_FOREACH (
const LineInfo &info, lineInfoList) {
706 QTextBlock block = info.block;
708 const QTextBlockFormat blockFormatDiff =
formatDifference(block.blockFormat(), mostCommonBlockFormat).toBlockFormat();
709 QTextCharFormat blockCharFormatDiff = QTextCharFormat();
711 if (formats.size()==1) {
712 blockCharFormatDiff =
formatDifference(formats.at(0).format, mostCommonCharFormat).toCharFormat();
716 blockCharFormatDiff.clearProperty(QTextBlockFormat::BlockAlignment);
720 const QTextLayout *layout = block.layout();
721 const QTextLine line = layout->lineAt(0);
722 if (!line.isValid()) {
730 svgWriter.writeStartElement(
"tspan");
732 const QString text = block.text();
735 switch (block.textDirection()) {
736 case Qt::LeftToRight:
737 isRightToLeft =
false;
739 case Qt::RightToLeft:
740 isRightToLeft =
true;
742 case Qt::LayoutDirectionAuto:
751 svgWriter.writeAttribute(
"direction",
"rtl");
752 svgWriter.writeAttribute(
"unicode-bidi",
"embed");
756 const QString blockStyleString =
style(blockCharFormatDiff,
760 if (!blockStyleString.isEmpty()) {
761 svgWriter.writeAttribute(
"style", blockStyleString);
771 Qt::Alignment blockAlignment = block.blockFormat().alignment();
773 if (blockAlignment & Qt::AlignLeft) {
774 blockAlignment &= ~Qt::AlignLeft;
775 blockAlignment |= Qt::AlignRight;
776 }
else if (blockAlignment & Qt::AlignRight) {
777 blockAlignment &= ~Qt::AlignRight;
778 blockAlignment |= Qt::AlignLeft;
782 if (blockAlignment & Qt::AlignHCenter) {
784 }
else if (blockAlignment & Qt::AlignRight) {
787 svgWriter.writeAttribute(
"x",
"0");
794 (prevBlockAscent + prevBlockDescent) * qreal(prevBlockRelativeLineSpacing) / 100.0;
796 const qreal currentLineSpacing = (info.numSkippedLines + 1) * lineHeightPt;
800 prevBlockRelativeLineSpacing =
801 blockFormatDiff.hasProperty(QTextFormat::LineHeight) ?
802 blockFormatDiff.lineHeight() :
803 mostCommonBlockFormat.lineHeight();
806 blockFormatDiff.hasProperty(QTextFormat::LineHeightType) ?
807 blockFormatDiff.lineHeightType() :
808 mostCommonBlockFormat.lineHeightType();
810 if (prevBlockLineType == QTextBlockFormat::SingleHeight) {
812 prevBlockRelativeLineSpacing = 100;
819 if (formats.size()>1) {
822 for (
int f=0; f<formats.size(); f++) {
824 for (
int c = 0; c<formats.at(f).
length; c++) {
825 chunk.append(text.at(formats.at(f).start+c));
828 charFormats.append(formats.at(f).format);
831 for (
int c = 0; c<texts.size(); c++) {
832 QTextCharFormat diff =
formatDifference(charFormats.at(c), mostCommonCharFormat).toCharFormat();
833 const QString subStyle =
style(diff, QTextBlockFormat(), mostCommonCharFormat);
834 if (!subStyle.isEmpty()) {
835 svgWriter.writeStartElement(
"tspan");
836 svgWriter.writeAttribute(
"style", subStyle);
837 svgWriter.writeCharacters(texts.at(c));
838 svgWriter.writeEndElement();
840 svgWriter.writeCharacters(texts.at(c));
845 svgWriter.writeCharacters(text);
851 svgWriter.writeCharacters(QLatin1String(
"\n"));
853 svgWriter.writeEndElement();
855 svgWriter.writeEndElement();
857 if (svgWriter.hasError()) {
858 d->errors << i18n(
"Unknown error writing SVG text element");
861 *svgText = QString::fromUtf8(svgBuffer.data()).trimmed();
866 QTextCharFormat &charFormat,
867 QTextBlockFormat &blockFormat,
873 QString presentationAttributes;
874 for (
int a = 0; a < elementAttributes.size(); a++) {
875 if (elementAttributes.at(a).name() !=
"style") {
876 presentationAttributes
877 .append(elementAttributes.at(a).name().toString())
879 .append(elementAttributes.at(a).value().toString())
884 if (presentationAttributes.endsWith(
";")) {
885 presentationAttributes.chop(1);
888 if (elementAttributes.hasAttribute(
"style")) {
889 styleString = elementAttributes.value(
"style").toString();
890 if (styleString.endsWith(
";")) {
895 if (!styleString.isEmpty() || !presentationAttributes.isEmpty()) {
897 styleString.append(
";")
898 .append(presentationAttributes);
903 charFormat = formats.at(0).toCharFormat();
904 blockFormat = formats.at(1).toBlockFormat();
910 QXmlStreamReader svgReader(svgText.trimmed());
912 QTextCursor cursor(doc);
914 struct BlockFormatRecord {
915 BlockFormatRecord() {}
916 BlockFormatRecord(QTextBlockFormat _blockFormat,
917 QTextCharFormat _charFormat)
918 : blockFormat(_blockFormat),
919 charFormat(_charFormat)
922 QTextBlockFormat blockFormat;
923 QTextCharFormat charFormat;
927 formatStack.push(BlockFormatRecord(QTextBlockFormat(), QTextCharFormat()));
928 cursor.setCharFormat(formatStack.top().charFormat);
929 cursor.setBlockFormat(formatStack.top().blockFormat);
931 qreal currBlockAbsoluteLineOffset = 0.0;
932 int prevBlockCursorPosition = -1;
933 Qt::Alignment prevBlockAlignment = Qt::AlignLeft;
934 bool prevTspanHasTrailingLF =
false;
935 qreal prevLineDescent = 0.0;
936 qreal prevLineAscent = 0.0;
938 boost::optional<qreal> previousBlockAbsoluteXOffset =
939 boost::optional<qreal>(
false, qreal());
941 std::optional<ExtraStyles> docExtraStyles;
943 while (!svgReader.atEnd()) {
944 QXmlStreamReader::TokenType token = svgReader.readNext();
946 case QXmlStreamReader::StartElement:
948 prevTspanHasTrailingLF =
false;
950 bool newBlock =
false;
951 QTextBlockFormat newBlockFormat;
952 QTextCharFormat newCharFormat;
953 qreal absoluteLineOffset = 1.0;
956 if (!formatStack.empty()) {
957 newBlockFormat = formatStack.top().blockFormat;
958 newCharFormat = formatStack.top().charFormat;
963 const QXmlStreamAttributes elementAttributes = svgReader.attributes();
966 if (!docExtraStyles && svgReader.name() == QLatin1String(
"text")) {
967 if (extraStyles.inlineSize > 0.0) {
975 docExtraStyles = extraStyles;
983 boost::optional<qreal> blockAbsoluteXOffset =
984 boost::make_optional(
false, qreal());
986 if (elementAttributes.hasAttribute(
"x")) {
987 QString xString = elementAttributes.value(
"x").toString();
988 if (xString.contains(
"pt")) {
989 xString = xString.remove(
"pt").trimmed();
996 Qt::Alignment thisBlockAlignment = Qt::AlignLeft;
997 if (newBlockFormat.hasProperty(QTextBlockFormat::BlockAlignment)) {
998 thisBlockAlignment = newBlockFormat.alignment();
999 }
else if (!formatStack.empty()) {
1000 thisBlockAlignment = formatStack.top().blockFormat.alignment();
1003 const auto isSameXOffset = [&]() {
1004 return previousBlockAbsoluteXOffset && blockAbsoluteXOffset
1005 &&
qFuzzyCompare(*previousBlockAbsoluteXOffset, *blockAbsoluteXOffset);
1007 if ((isSameXOffset() || thisBlockAlignment != prevBlockAlignment) && svgReader.name() !=
"text"
1008 && elementAttributes.hasAttribute(
"dy")) {
1010 QString dyString = elementAttributes.value(
"dy").toString();
1011 if (dyString.contains(
"pt")) {
1012 dyString = dyString.remove(
"pt").trimmed();
1018 newBlock = absoluteLineOffset > 0;
1021 if (elementAttributes.hasAttribute(
"x")) {
1022 previousBlockAbsoluteXOffset = blockAbsoluteXOffset;
1024 prevBlockAlignment = thisBlockAlignment;
1028 doc->setTextWidth(100);
1029 doc->setTextWidth(-1);
1031 if (newBlock && absoluteLineOffset > 0) {
1035 QTextLine line = cursor.block().layout()->lineAt(0);
1037 if (prevBlockCursorPosition >= 0) {
1039 prevBlockCursorPosition, currBlockAbsoluteLineOffset);
1042 prevBlockCursorPosition = cursor.position();
1043 prevLineAscent = line.ascent();
1044 prevLineDescent = line.descent();
1045 currBlockAbsoluteLineOffset = absoluteLineOffset;
1047 cursor.insertBlock();
1048 cursor.setCharFormat(formatStack.top().charFormat);
1049 cursor.setBlockFormat(formatStack.top().blockFormat);
1052 cursor.mergeCharFormat(newCharFormat);
1053 cursor.mergeBlockFormat(newBlockFormat);
1055 formatStack.push(BlockFormatRecord(cursor.blockFormat(), cursor.charFormat()));
1059 case QXmlStreamReader::EndElement:
1061 if (svgReader.name() !=
"text") {
1065 cursor.setCharFormat(formatStack.top().charFormat);
1077 && prevTspanHasTrailingLF) {
1078 cursor.setBlockFormat(formatStack.top().blockFormat);
1080 prevTspanHasTrailingLF =
false;
1084 case QXmlStreamReader::Characters:
1086 cursor.insertText(svgReader.text().toString());
1087 prevTspanHasTrailingLF = svgReader.text().endsWith(
'\n');
1095 if (prevBlockCursorPosition >= 0) {
1096 QTextLine line = cursor.block().layout()->lineAt(0);
1098 prevBlockCursorPosition, currBlockAbsoluteLineOffset);
1102 if (!docExtraStyles) {
1105 QTextFrameFormat f = doc->rootFrame()->frameFormat();
1108 doc->rootFrame()->setFrameFormat(f);
1111 if (svgReader.hasError()) {
1112 d->errors << svgReader.errorString();
1115 doc->setModified(
false);
1175 QTextBlockFormat blockFormat,
1176 QTextCharFormat mostCommon,
1177 const bool includeLineHeight)
1180 for(
int i=0; i<format.properties().size(); i++) {
1182 int propertyId = format.properties().keys().at(i);
1184 if (propertyId == QTextCharFormat::FontFamily) {
1185 const QString fontFamily = format.properties()[propertyId].toString();
1186 c.append(
"font-family").append(
":").append(fontFamily);
1188 if (propertyId == QTextCharFormat::FontPointSize ||
1189 propertyId == QTextCharFormat::FontPixelSize) {
1193 c.append(
"font-size").append(
":")
1194 .append(format.properties()[propertyId].toString());
1196 if (propertyId == QTextCharFormat::FontWeight) {
1199 int convertedWeight = 400;
1201 switch (format.properties()[propertyId].toInt()) {
1202 case QFont::Weight::Thin:
1203 convertedWeight = 100;
1205 case QFont::Weight::ExtraLight:
1206 convertedWeight = 200;
1208 case QFont::Weight::Light:
1209 convertedWeight = 300;
1211 case QFont::Weight::Normal:
1212 convertedWeight = 400;
1214 case QFont::Weight::Medium:
1215 convertedWeight = 500;
1217 case QFont::Weight::DemiBold:
1218 convertedWeight = 600;
1220 case QFont::Weight::Bold:
1221 convertedWeight = 700;
1223 case QFont::Weight::ExtraBold:
1224 convertedWeight = 800;
1226 case QFont::Weight::Black:
1227 convertedWeight = 900;
1230 warnFile <<
"WARNING: Invalid QFont::Weight value supplied to KoSvgTextShapeMarkupConverter::style.";
1234 c.append(
"font-weight").append(
":")
1235 .append(QString::number(convertedWeight));
1237 if (propertyId == QTextCharFormat::FontItalic) {
1238 QString val =
"italic";
1239 if (!format.fontItalic()) {
1242 c.append(
"font-style").append(
":")
1246 if (propertyId == QTextCharFormat::FontCapitalization) {
1247 if (format.fontCapitalization() == QFont::SmallCaps){
1248 c.append(
"font-variant").append(
":")
1249 .append(
"small-caps");
1250 }
else if (format.fontCapitalization() == QFont::AllUppercase) {
1251 c.append(
"text-transform").append(
":")
1252 .append(
"uppercase");
1253 }
else if (format.fontCapitalization() == QFont::AllLowercase) {
1254 c.append(
"text-transform").append(
":")
1255 .append(
"lowercase");
1256 }
else if (format.fontCapitalization() == QFont::Capitalize) {
1257 c.append(
"text-transform").append(
":")
1258 .append(
"capitalize");
1262 if (propertyId == QTextCharFormat::FontStretch) {
1263 QString valueString = QString::number(format.fontStretch(), 10);
1264 if (format.fontStretch() == QFont::ExtraCondensed) {
1265 valueString =
"extra-condensed";
1266 }
else if (format.fontStretch() == QFont::SemiCondensed) {
1267 valueString =
"semi-condensed";
1268 }
else if (format.fontStretch() == QFont::Condensed) {
1269 valueString =
"condensed";
1270 }
else if (format.fontStretch() == QFont::AnyStretch) {
1271 valueString =
"normal";
1272 }
else if (format.fontStretch() == QFont::Expanded) {
1273 valueString =
"expanded";
1274 }
else if (format.fontStretch() == QFont::SemiExpanded) {
1275 valueString =
"semi-expanded";
1276 }
else if (format.fontStretch() == QFont::ExtraExpanded) {
1277 valueString =
"extra-expanded";
1278 }
else if (format.fontStretch() == QFont::UltraExpanded) {
1279 valueString =
"ultra-expanded";
1281 c.append(
"font-stretch").append(
":")
1282 .append(valueString);
1284 if (propertyId == QTextCharFormat::FontKerning) {
1286 if (format.fontKerning()) {
1291 c.append(
"kerning").append(
":")
1294 if (propertyId == QTextCharFormat::FontWordSpacing) {
1295 c.append(
"word-spacing").append(
":")
1296 .append(QString::number(format.fontWordSpacing()));
1298 if (propertyId == QTextCharFormat::FontLetterSpacing) {
1300 if (format.fontLetterSpacingType()==QFont::AbsoluteSpacing) {
1301 val = QString::number(format.fontLetterSpacing());
1303 val = QString::number(((format.fontLetterSpacing()/100)*format.fontPointSize()));
1305 c.append(
"letter-spacing").append(
":")
1308 if (propertyId == QTextCharFormat::TextOutline) {
1309 if (format.textOutline().color() != mostCommon.textOutline().color()) {
1310 c.append(
"stroke").append(
":")
1311 .append(format.textOutline().color().name());
1315 if (format.textOutline().width() != mostCommon.textOutline().width()) {
1316 c.append(
"stroke-width").append(
":")
1317 .append(QString::number(format.textOutline().width()));
1322 if (propertyId == QTextCharFormat::TextVerticalAlignment) {
1323 QString val =
"baseline";
1324 if (format.verticalAlignment() == QTextCharFormat::AlignSubScript) {
1325 val = QLatin1String(
"sub");
1327 else if (format.verticalAlignment() == QTextCharFormat::AlignSuperScript) {
1328 val = QLatin1String(
"super");
1330 c.append(
"baseline-shift").append(
":").append(val);
1333 if (propertyId == QTextCharFormat::ForegroundBrush) {
1334 QColor::NameFormat colorFormat;
1336 if (format.foreground().color().alphaF() < 1.0) {
1337 colorFormat = QColor::HexArgb;
1339 colorFormat = QColor::HexRgb;
1342 c.append(
"fill").append(
":")
1343 .append(format.foreground().color().name(colorFormat));
1359 if (blockFormat.hasProperty(QTextBlockFormat::BlockAlignment)) {
1365 if (blockFormat.alignment()==Qt::AlignRight) {
1367 }
else if (blockFormat.alignment()==Qt::AlignCenter) {
1372 c.append(
"text-anchor").append(
":")
1379 if (includeLineHeight && blockFormat.hasProperty(QTextBlockFormat::LineHeight)) {
1381 if (blockFormat.lineHeightType() == QTextBlockFormat::ProportionalHeight) {
1382 h = blockFormat.lineHeight() / 100.0;
1383 }
else if (blockFormat.lineHeightType() == QTextBlockFormat::SingleHeight) {
1386 QString c =
"line-height:";
1388 c += QString::number(blockFormat.lineHeight() / 100.0);
1395 return style.join(
"; ");
1399 QTextCharFormat currentCharFormat,
1400 QTextBlockFormat currentBlockFormat,
1403 Q_UNUSED(currentBlockFormat);
1406 QTextCharFormat charFormat;
1407 charFormat.setTextOutline(currentCharFormat.textOutline());
1408 QTextBlockFormat blockFormat;
1412 for (
int i=0; i<styles.size(); i++) {
1413 if (!styles.at(i).isEmpty()){
1417 if (
style.size() < 2) {
1421 QString
property =
style.at(0).trimmed();
1424 if (property ==
"font-family") {
1425 charFormat.setFontFamily(
value);
1428 if (property ==
"font-size") {
1430 charFormat.setFontPointSize(val);
1433 if (property ==
"font-variant") {
1434 if (
value==
"small-caps") {
1435 charFormat.setFontCapitalization(QFont::SmallCaps);
1437 charFormat.setFontCapitalization(QFont::MixedCase);
1441 if (property ==
"font-style") {
1443 charFormat.setFontItalic(
true);
1445 charFormat.setFontItalic(
false);
1449 if (property ==
"font-stretch") {
1450 if (
value ==
"ultra-condensed") {
1451 charFormat.setFontStretch(QFont::UltraCondensed);
1452 }
else if (
value ==
"condensed") {
1453 charFormat.setFontStretch(QFont::Condensed);
1454 }
else if (
value ==
"semi-condensed") {
1455 charFormat.setFontStretch(QFont::SemiCondensed);
1456 }
else if (
value ==
"normal") {
1457 charFormat.setFontStretch(100);
1458 }
else if (
value ==
"semi-expanded") {
1459 charFormat.setFontStretch(QFont::SemiExpanded);
1460 }
else if (
value ==
"expanded") {
1461 charFormat.setFontStretch(QFont::Expanded);
1462 }
else if (
value ==
"extra-expanded") {
1463 charFormat.setFontStretch(QFont::ExtraExpanded);
1464 }
else if (
value ==
"ultra-expanded") {
1465 charFormat.setFontStretch(QFont::UltraExpanded);
1467 charFormat.setFontStretch(
value.toInt());
1471 if (property ==
"font-weight") {
1474 int convertedWeight = QFont::Weight::Normal;
1476 switch (
value.toInt()) {
1478 convertedWeight = QFont::Weight::Thin;
1481 convertedWeight = QFont::Weight::ExtraLight;
1484 convertedWeight = QFont::Weight::Light;
1487 convertedWeight = QFont::Weight::Normal;
1490 convertedWeight = QFont::Weight::Medium;
1493 convertedWeight = QFont::Weight::DemiBold;
1496 convertedWeight = QFont::Weight::Bold;
1499 convertedWeight = QFont::Weight::ExtraBold;
1502 convertedWeight = QFont::Weight::Black;
1505 warnFile <<
"WARNING: Invalid weight value supplied to KoSvgTextShapeMarkupConverter::stylesFromString.";
1509 charFormat.setFontWeight(convertedWeight);
1512 if (property ==
"text-decoration") {
1513 charFormat.setFontUnderline(
false);
1514 charFormat.setFontOverline(
false);
1515 charFormat.setFontStrikeOut(
false);
1517 if (values.contains(
"line-through")) {
1518 charFormat.setFontStrikeOut(
true);
1520 if (values.contains(
"overline")) {
1521 charFormat.setFontOverline(
true);
1523 if(values.contains(
"underline")){
1524 charFormat.setFontUnderline(
true);
1528 if (property ==
"text-transform") {
1529 if (
value ==
"uppercase") {
1530 charFormat.setFontCapitalization(QFont::AllUppercase);
1531 }
else if (
value ==
"lowercase") {
1532 charFormat.setFontCapitalization(QFont::AllLowercase);
1533 }
else if (
value ==
"capitalize") {
1534 charFormat.setFontCapitalization(QFont::Capitalize);
1536 charFormat.setFontCapitalization(QFont::MixedCase);
1540 if (property ==
"letter-spacing") {
1542 charFormat.setFontLetterSpacingType(QFont::AbsoluteSpacing);
1543 charFormat.setFontLetterSpacing(val);
1546 if (property ==
"word-spacing") {
1548 charFormat.setFontWordSpacing(val);
1551 if (property ==
"kerning") {
1552 if (
value ==
"auto") {
1553 charFormat.setFontKerning(
true);
1556 charFormat.setFontKerning(
false);
1557 charFormat.setFontLetterSpacingType(QFont::AbsoluteSpacing);
1558 charFormat.setFontLetterSpacing(charFormat.fontLetterSpacing() + val);
1562 if (property ==
"stroke") {
1563 QPen pen = charFormat.textOutline();
1565 color.setNamedColor(
value);
1566 pen.setColor(color);
1567 charFormat.setTextOutline(pen);
1570 if (property ==
"stroke-width") {
1571 QPen pen = charFormat.textOutline();
1572 pen.setWidth(
value.toInt());
1573 charFormat.setTextOutline(pen);
1576 if (property ==
"fill") {
1578 color.setNamedColor(
value);
1581 if (!color.isValid()) {
1586 qreal currentAlpha = charFormat.foreground().color().alphaF();
1589 if (currentAlpha < 1.0) {
1591 if (color.alphaF() < 1.0) {
1592 color.setAlphaF(currentAlpha);
1596 charFormat.setForeground(color);
1599 if (property ==
"fill-opacity") {
1600 QColor color = charFormat.foreground().color();
1607 alpha = color.alphaF();
1609 color.setAlphaF(alpha);
1610 charFormat.setForeground(color);
1613 if (property ==
"text-anchor") {
1614 if (
value ==
"end") {
1615 blockFormat.setAlignment(Qt::AlignRight);
1616 }
else if (
value ==
"middle") {
1617 blockFormat.setAlignment(Qt::AlignCenter);
1619 blockFormat.setAlignment(Qt::AlignLeft);
1623 if (property ==
"baseline-shift") {
1624 if (
value ==
"super") {
1625 charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
1626 }
else if (
value ==
"sub") {
1627 charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript);
1629 charFormat.setVerticalAlignment(QTextCharFormat::AlignNormal);
1633 if (property ==
"line-height") {
1634 double lineHeightPercent = -1.0;
1636 if (
value.endsWith(
'%')) {
1640 lineHeightPercent =
value.left(
value.length() - 1).toDouble(&ok);
1642 lineHeightPercent = -1.0;
1644 }
else if(
const double unitless =
value.toDouble(&ok); ok) {
1645 lineHeightPercent = unitless * 100.0;
1646 }
else if (
value == QLatin1String(
"normal")) {
1647 lineHeightPercent = -1.0;
1648 blockFormat.setLineHeight(1, QTextBlockFormat::SingleHeight);
1650 if (lineHeightPercent >= 0) {
1651 blockFormat.setLineHeight(lineHeightPercent, QTextBlockFormat::ProportionalHeight);
1655 if (property ==
"inline-size") {
1662 if (property ==
"white-space") {
1663 if (
value == QLatin1String(
"pre")) {
1665 }
else if (
value == QLatin1String(
"pre-wrap")) {
1674 formats.append(charFormat);
1675 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.