Krita Source Code Documentation
Loading...
Searching...
No Matches
KoSvgTextShapeMarkupConverter.cpp File Reference
#include "KoSvgTextShapeMarkupConverter.h"
#include "klocalizedstring.h"
#include "kis_assert.h"
#include "kis_debug.h"
#include <ft2build.h>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QBuffer>
#include <QTextCodec>
#include <QtMath>
#include <QTextBlock>
#include <QTextLayout>
#include <QTextLine>
#include <QFont>
#include <QStack>
#include <QStringView>
#include <KoSvgTextShape.h>
#include <KoXmlWriter.h>
#include <KoDocumentResourceManager.h>
#include <KoColor.h>
#include <SvgParser.h>
#include <SvgWriter.h>
#include <SvgUtil.h>
#include <SvgSavingContext.h>
#include <SvgGraphicContext.h>
#include <html/HtmlSavingContext.h>
#include <html/HtmlWriter.h>
#include "kis_dom_utils.h"
#include <boost/optional.hpp>
#include <FlakeDebug.h>

Go to the source code of this file.

Classes

struct  KoSvgTextShapeMarkupConverter::ExtraStyles
 
struct  KoSvgTextShapeMarkupConverter::Private
 

Functions

qreal calcLineWidth (const QTextBlock &block)
 
bool compareFormatUnderlineWithMostCommon (QTextCharFormat format, QTextCharFormat mostCommon)
 
QString convertFormatUnderlineToSvg (QTextCharFormat format)
 
QTextFormat findMostCommonFormat (const QList< QTextFormat > &allFormats)
 
qreal fixFromQtDpi (qreal value)
 
qreal fixToQtDpi (qreal value)
 
static bool guessIsRightToLeft (QStringView text)
 
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)
 
Q_GUI_EXPORT int qt_defaultDpi ()
 

Function Documentation

◆ calcLineWidth()

qreal calcLineWidth ( const QTextBlock & block)

Definition at line 539 of file KoSvgTextShapeMarkupConverter.cpp.

540{
541 const QString blockText = block.text();
542
543 QTextLayout lineLayout;
544 lineLayout.setText(blockText);
545 lineLayout.setFont(block.charFormat().font());
546 lineLayout.setFormats(block.textFormats());
547 lineLayout.setTextOption(block.layout()->textOption());
548
549 lineLayout.beginLayout();
550 QTextLine fullLine = lineLayout.createLine();
551 if (!fullLine.isValid()) {
552 fullLine.setNumColumns(blockText.size());
553 }
554 lineLayout.endLayout();
555
556 return fixFromQtDpi(lineLayout.boundingRect().width());
557}
qreal fixFromQtDpi(qreal value)

References fixFromQtDpi().

◆ compareFormatUnderlineWithMostCommon()

bool compareFormatUnderlineWithMostCommon ( QTextCharFormat format,
QTextCharFormat mostCommon )

Definition at line 1135 of file KoSvgTextShapeMarkupConverter.cpp.

1136{
1137 // color and style is not supported in rich text editor yet
1138 // TODO: support color and style
1139 return format.fontUnderline() == mostCommon.fontUnderline()
1140 && format.fontOverline() == mostCommon.fontOverline()
1141 && format.fontStrikeOut() == mostCommon.fontStrikeOut();
1142}

◆ convertFormatUnderlineToSvg()

QString convertFormatUnderlineToSvg ( QTextCharFormat format)

Definition at line 1144 of file KoSvgTextShapeMarkupConverter.cpp.

1145{
1146 // color and style is not supported in rich text editor yet
1147 // and text-decoration-line and -style and -color are not supported in svg render either
1148 // hence we just use text-decoration
1149 // TODO: support color and style
1150 QStringList line;
1151
1152 if (format.fontUnderline()) {
1153 line.append("underline");
1154 if (format.underlineStyle() != QTextCharFormat::SingleUnderline) {
1155 warnFile << "Krita only supports solid underline style";
1156 }
1157 }
1158
1159 if (format.fontOverline()) {
1160 line.append("overline");
1161 }
1162
1163 if (format.fontStrikeOut()) {
1164 line.append("line-through");
1165 }
1166
1167 if (line.isEmpty())
1168 {
1169 line.append("none");
1170 }
1171
1172 QString c = QString("text-decoration").append(":")
1173 .append(line.join(" "));
1174
1175 return c;
1176}
#define warnFile
Definition kis_debug.h:95

References warnFile.

◆ findMostCommonFormat()

QTextFormat findMostCommonFormat ( const QList< QTextFormat > & allFormats)

Get all existing property ids

Filter out properties that do not exist in some formats. Otherwise, the global format may override the default value used in these formats (and yes, we do not have access to the default values to use them in difference calculation algorithm

Calculate the frequency of values used in all the formats

Add the most popular property value to the set of most common properties

Definition at line 438 of file KoSvgTextShapeMarkupConverter.cpp.

439{
440 QTextCharFormat mostCommonFormat;
441
442 QSet<int> propertyIds;
443
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);
451 }
452 }
453
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);
464 } else {
465 ++it;
466 }
467 }
468 if (propertyIds.isEmpty()) break;
469 }
470
471 if (!propertyIds.isEmpty()) {
472 QMap<int, QList<std::pair<QVariant, int>>> propertyFrequency;
473
477 Q_FOREACH (const QTextFormat &format, allFormats) {
478 const QMap<int, QVariant> formatProperties = format.properties();
479
480 Q_FOREACH (int id, propertyIds) {
481 KIS_SAFE_ASSERT_RECOVER_BREAK(formatProperties.contains(id));
482 QList<std::pair<QVariant, int>> &valueFrequencies = propertyFrequency[id];
483 const QVariant formatPropValue = formatProperties.value(id);
484
485 // Find the value in frequency table
486 auto it = std::find_if(valueFrequencies.begin(), valueFrequencies.end(),
487 [formatPropValue](const std::pair<QVariant, int> &element) { return element.first == formatPropValue; });
488
489 if (it != valueFrequencies.end()) {
490 // Increase frequency by 1, if already met
491 it->second += 1;
492 } else {
493 // Add with initial frequency of 1 if met for the first time
494 valueFrequencies.push_back({formatPropValue, 1});
495 }
496 }
497 }
498
502 for (auto it = propertyFrequency.constBegin(); it != propertyFrequency.constEnd(); ++it) {
503 const int id = it.key();
504 const QList<std::pair<QVariant, int>>& allValues = it.value();
505
506 int maxCount = 0;
507 QVariant maxValue;
508
509 for (const auto& [propValue, valFrequency] : allValues) {
510 if (valFrequency > maxCount) {
511 maxCount = valFrequency;
512 maxValue = propValue;
513 }
514 }
515
516 KIS_SAFE_ASSERT_RECOVER_BREAK(maxCount > 0);
517 mostCommonFormat.setProperty(id, maxValue);
518 }
519
520 }
521
522 return mostCommonFormat;
523}
#define KIS_SAFE_ASSERT_RECOVER_BREAK(cond)
Definition kis_assert.h:127

References KIS_SAFE_ASSERT_RECOVER_BREAK.

◆ fixFromQtDpi()

qreal fixFromQtDpi ( qreal value)

Definition at line 527 of file KoSvgTextShapeMarkupConverter.cpp.

528{
529 // HACK ALERT: see a comment in convertDocumentToSvg!
530 return value * 72.0 / qt_defaultDpi();
531}
float value(const T *src, size_t ch)
Q_GUI_EXPORT int qt_defaultDpi()

References qt_defaultDpi(), and value().

◆ fixToQtDpi()

qreal fixToQtDpi ( qreal value)

Definition at line 533 of file KoSvgTextShapeMarkupConverter.cpp.

534{
535 // HACK ALERT: see a comment in convertDocumentToSvg!
536 return value * qt_defaultDpi() / 72.0;
537}

References qt_defaultDpi(), and value().

◆ guessIsRightToLeft()

static bool guessIsRightToLeft ( QStringView text)
static

Mind blowing part: QTextEdit uses a hi-end algorithm for auto-estimation for the text directionality, so the user expects his text being saved to SVG with the same directionality. Just emulate behavior of direction="auto", which is not supported by SVG 1.1

BUG: 392064

Definition at line 567 of file KoSvgTextShapeMarkupConverter.cpp.

567 {
568 // Is this just a worse version of QString::isRightToLeft??
569 for (int i = 0; i < text.size(); i++) {
570 const QChar ch = text[i];
571 if (ch.direction() == QChar::DirR || ch.direction() == QChar::DirAL) {
572 return true;
573 } else if (ch.direction() == QChar::DirL) {
574 return false;
575 }
576 }
577 return false;
578}

◆ parseTextAttributes()

void parseTextAttributes ( const QXmlStreamAttributes & elementAttributes,
QTextCharFormat & charFormat,
QTextBlockFormat & blockFormat,
KoSvgTextShapeMarkupConverter::ExtraStyles & extraStyles )

Definition at line 869 of file KoSvgTextShapeMarkupConverter.cpp.

873{
874 QString styleString;
875
876 // we convert all the presentation attributes into styles
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())
882 .append(":")
883 .append(elementAttributes.at(a).value().toString())
884 .append(";");
885 }
886 }
887
888 if (presentationAttributes.endsWith(";")) {
889 presentationAttributes.chop(1);
890 }
891
892 if (elementAttributes.hasAttribute("style")) {
893 styleString = elementAttributes.value("style").toString();
894 if (styleString.endsWith(";")) {
895 styleString.chop(1);
896 }
897 }
898
899 if (!styleString.isEmpty() || !presentationAttributes.isEmpty()) {
900 //add attributes to parse them as part of the style.
901 styleString.append(";")
902 .append(presentationAttributes);
903 QStringList styles = styleString.split(";");
904 QVector<QTextFormat> formats =
905 KoSvgTextShapeMarkupConverter::stylesFromString(styles, charFormat, blockFormat, extraStyles);
906
907 charFormat = formats.at(0).toCharFormat();
908 blockFormat = formats.at(1).toBlockFormat();
909 }
910}
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...

References KoSvgTextShapeMarkupConverter::stylesFromString().

◆ postCorrectBlockHeight()

void postCorrectBlockHeight ( QTextDocument * doc,
qreal currLineAscent,
qreal prevLineAscent,
qreal prevLineDescent,
int prevBlockCursorPosition,
qreal currentBlockAbsoluteLineOffset )

Definition at line 415 of file KoSvgTextShapeMarkupConverter.cpp.

421{
422 KIS_SAFE_ASSERT_RECOVER_RETURN(prevBlockCursorPosition >= 0);
423
424 QTextCursor postCorrectionCursor(doc);
425 postCorrectionCursor.setPosition(prevBlockCursorPosition);
426 if (!postCorrectionCursor.isNull()) {
427 const qreal relativeLineHeight =
428 ((currentBlockAbsoluteLineOffset - currLineAscent + prevLineAscent) /
429 (prevLineAscent + prevLineDescent)) * 100.0;
430
431 QTextBlockFormat format = postCorrectionCursor.blockFormat();
432 format.setLineHeight(relativeLineHeight, QTextBlockFormat::ProportionalHeight);
433 postCorrectionCursor.setBlockFormat(format);
434 postCorrectionCursor = QTextCursor();
435 }
436}
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128

References KIS_SAFE_ASSERT_RECOVER_RETURN.

◆ qt_defaultDpi()

Q_GUI_EXPORT int qt_defaultDpi ( )