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 535 of file KoSvgTextShapeMarkupConverter.cpp.

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

References fixFromQtDpi().

◆ compareFormatUnderlineWithMostCommon()

bool compareFormatUnderlineWithMostCommon ( QTextCharFormat format,
QTextCharFormat mostCommon )

Definition at line 1131 of file KoSvgTextShapeMarkupConverter.cpp.

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

◆ convertFormatUnderlineToSvg()

QString convertFormatUnderlineToSvg ( QTextCharFormat format)

Definition at line 1140 of file KoSvgTextShapeMarkupConverter.cpp.

1141{
1142 // color and style is not supported in rich text editor yet
1143 // and text-decoration-line and -style and -color are not supported in svg render either
1144 // hence we just use text-decoration
1145 // TODO: support color and style
1146 QStringList line;
1147
1148 if (format.fontUnderline()) {
1149 line.append("underline");
1150 if (format.underlineStyle() != QTextCharFormat::SingleUnderline) {
1151 warnFile << "Krita only supports solid underline style";
1152 }
1153 }
1154
1155 if (format.fontOverline()) {
1156 line.append("overline");
1157 }
1158
1159 if (format.fontStrikeOut()) {
1160 line.append("line-through");
1161 }
1162
1163 if (line.isEmpty())
1164 {
1165 line.append("none");
1166 }
1167
1168 QString c = QString("text-decoration").append(":")
1169 .append(line.join(" "));
1170
1171 return c;
1172}
#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 434 of file KoSvgTextShapeMarkupConverter.cpp.

435{
436 QTextCharFormat mostCommonFormat;
437
438 QSet<int> propertyIds;
439
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);
447 }
448 }
449
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);
460 } else {
461 ++it;
462 }
463 }
464 if (propertyIds.isEmpty()) break;
465 }
466
467 if (!propertyIds.isEmpty()) {
468 QMap<int, QList<std::pair<QVariant, int>>> propertyFrequency;
469
473 Q_FOREACH (const QTextFormat &format, allFormats) {
474 const QMap<int, QVariant> formatProperties = format.properties();
475
476 Q_FOREACH (int id, propertyIds) {
477 KIS_SAFE_ASSERT_RECOVER_BREAK(formatProperties.contains(id));
478 QList<std::pair<QVariant, int>> &valueFrequencies = propertyFrequency[id];
479 const QVariant formatPropValue = formatProperties.value(id);
480
481 // Find the value in frequency table
482 auto it = std::find_if(valueFrequencies.begin(), valueFrequencies.end(),
483 [formatPropValue](const std::pair<QVariant, int> &element) { return element.first == formatPropValue; });
484
485 if (it != valueFrequencies.end()) {
486 // Increase frequency by 1, if already met
487 it->second += 1;
488 } else {
489 // Add with initial frequency of 1 if met for the first time
490 valueFrequencies.push_back({formatPropValue, 1});
491 }
492 }
493 }
494
498 for (auto it = propertyFrequency.constBegin(); it != propertyFrequency.constEnd(); ++it) {
499 const int id = it.key();
500 const QList<std::pair<QVariant, int>>& allValues = it.value();
501
502 int maxCount = 0;
503 QVariant maxValue;
504
505 for (const auto& [propValue, valFrequency] : allValues) {
506 if (valFrequency > maxCount) {
507 maxCount = valFrequency;
508 maxValue = propValue;
509 }
510 }
511
512 KIS_SAFE_ASSERT_RECOVER_BREAK(maxCount > 0);
513 mostCommonFormat.setProperty(id, maxValue);
514 }
515
516 }
517
518 return mostCommonFormat;
519}
#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 523 of file KoSvgTextShapeMarkupConverter.cpp.

524{
525 // HACK ALERT: see a comment in convertDocumentToSvg!
526 return value * 72.0 / qt_defaultDpi();
527}
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 529 of file KoSvgTextShapeMarkupConverter.cpp.

530{
531 // HACK ALERT: see a comment in convertDocumentToSvg!
532 return value * qt_defaultDpi() / 72.0;
533}

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 563 of file KoSvgTextShapeMarkupConverter.cpp.

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

◆ parseTextAttributes()

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

Definition at line 865 of file KoSvgTextShapeMarkupConverter.cpp.

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

417{
418 KIS_SAFE_ASSERT_RECOVER_RETURN(prevBlockCursorPosition >= 0);
419
420 QTextCursor postCorrectionCursor(doc);
421 postCorrectionCursor.setPosition(prevBlockCursorPosition);
422 if (!postCorrectionCursor.isNull()) {
423 const qreal relativeLineHeight =
424 ((currentBlockAbsoluteLineOffset - currLineAscent + prevLineAscent) /
425 (prevLineAscent + prevLineDescent)) * 100.0;
426
427 QTextBlockFormat format = postCorrectionCursor.blockFormat();
428 format.setLineHeight(relativeLineHeight, QTextBlockFormat::ProportionalHeight);
429 postCorrectionCursor.setBlockFormat(format);
430 postCorrectionCursor = QTextCursor();
431 }
432}
#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 ( )