diff --git a/sources/editor/graphicspart/partdynamictextfield.cpp b/sources/editor/graphicspart/partdynamictextfield.cpp index eea11ae89..f8dece784 100644 --- a/sources/editor/graphicspart/partdynamictextfield.cpp +++ b/sources/editor/graphicspart/partdynamictextfield.cpp @@ -215,7 +215,7 @@ void PartDynamicTextField::fromXml(const QDomElement &dom_elmt) { if (dom_elmt.hasAttribute("font")) { QFont font_; - font_.fromString(dom_elmt.attribute("font")); + QETUtils::fontFromString(font_, dom_elmt.attribute("font")); setFont(font_); } else if (dom_elmt.hasAttribute("font_size")) { diff --git a/sources/editor/graphicspart/parttext.cpp b/sources/editor/graphicspart/parttext.cpp index 3b6cc8e6e..7f4112d86 100644 --- a/sources/editor/graphicspart/parttext.cpp +++ b/sources/editor/graphicspart/parttext.cpp @@ -126,7 +126,7 @@ void PartText::fromXml(const QDomElement &xml_element) { } else if (xml_element.hasAttribute("font")) { QFont font_; - font_.fromString(xml_element.attribute("font")); + QETUtils::fontFromString(font_, xml_element.attribute("font")); setFont(font_); } diff --git a/sources/factory/elementpicturefactory.cpp b/sources/factory/elementpicturefactory.cpp index a9b9c2bc7..25c3b133a 100644 --- a/sources/factory/elementpicturefactory.cpp +++ b/sources/factory/elementpicturefactory.cpp @@ -21,6 +21,7 @@ #include "../editor/graphicspart/partline.h" #include "../qetapp.h" #include "../qetversion.h" +#include "../utils/qetutils.h" #include #include @@ -503,7 +504,7 @@ void ElementPictureFactory::parseText(const QDomElement &dom, QPainter &painter, font_ = QETApp::diagramTextsFont(dom.attribute("size").toDouble()); } else if (dom.hasAttribute("font")) { - font_.fromString(dom.attribute("font")); + QETUtils::fontFromString(font_, dom.attribute("font")); } QColor text_color(dom.attribute("color", "#000000")); diff --git a/sources/factory/ui/addtabledialog.cpp b/sources/factory/ui/addtabledialog.cpp index 325b6c758..7c5624383 100644 --- a/sources/factory/ui/addtabledialog.cpp +++ b/sources/factory/ui/addtabledialog.cpp @@ -275,7 +275,7 @@ void AddTableDialog::loadConfig() default: ui->m_header_alignment_cb->setCurrentIndex(2); } - m_header_font.fromString(header_object.value("font").toString()); + QETUtils::fontFromString(m_header_font, header_object.value("font").toString()); ui->m_header_font_pb->setText(m_header_font.family()); //Table @@ -292,7 +292,7 @@ void AddTableDialog::loadConfig() default: ui->m_table_alignment_cb->setCurrentIndex(2); } - m_table_font.fromString(table_object.value("font").toString()); + QETUtils::fontFromString(m_table_font, table_object.value("font").toString()); ui->m_table_font_pb->setText(m_table_font.family()); } diff --git a/sources/properties/terminaldata.cpp b/sources/properties/terminaldata.cpp index d06babc5c..2b9cd36c2 100644 --- a/sources/properties/terminaldata.cpp +++ b/sources/properties/terminaldata.cpp @@ -188,7 +188,7 @@ bool TerminalData::fromXml (const QDomElement &xml_element) QString font_str = xml_element.attribute("label_font"); if (!font_str.isEmpty()) - m_label_font.fromString(font_str); + QETUtils::fontFromString(m_label_font, font_str); m_label_rotation = xml_element.attribute("label_rotation", "0").toDouble(); diff --git a/sources/qetapp.cpp b/sources/qetapp.cpp index 83d26f9d4..deb20b161 100644 --- a/sources/qetapp.cpp +++ b/sources/qetapp.cpp @@ -26,6 +26,7 @@ #include "projectview.h" #include "qetdiagrameditor.h" #include "qeticons.h" +#include "utils/qetutils.h" #include "qetmessagebox.h" #include "qetproject.h" #include "qtextorientationspinboxwidget.h" @@ -1425,7 +1426,7 @@ QFont QETApp::diagramTextsItemFont(qreal size) //Font to use QFont font_ = diagramTextsItemFont(); if (settings.contains("diagrameditor/dynamic_text_font")) { - font_.fromString(settings.value( + QETUtils::fontFromString(font_, settings.value( "diagrameditor/dynamic_text_font" ).toString()); } @@ -1449,7 +1450,7 @@ QFont QETApp::indiTextsItemFont(qreal size) //Font to use QFont font_ = diagramTextsItemFont(); if (settings.contains("diagrameditor/independent_text_font")) { - font_.fromString(settings.value( + QETUtils::fontFromString(font_, settings.value( "diagrameditor/independent_text_font" ).toString()); } diff --git a/sources/qetgraphicsitem/ViewItem/projectdbmodel.cpp b/sources/qetgraphicsitem/ViewItem/projectdbmodel.cpp index b01f73a17..7ef031504 100644 --- a/sources/qetgraphicsitem/ViewItem/projectdbmodel.cpp +++ b/sources/qetgraphicsitem/ViewItem/projectdbmodel.cpp @@ -291,7 +291,7 @@ void ProjectDBModel::fromXml(const QDomElement &element) //Index 0,0 auto index_00 = element.firstChildElement("index00"); QFont font_; - font_.fromString(index_00.attribute("font")); + QETUtils::fontFromString(font_, index_00.attribute("font")); m_index_0_0_data.insert(Qt::FontRole, font_); auto me = QMetaEnum::fromType(); m_index_0_0_data.insert(Qt::TextAlignmentRole, me.keyToValue(index_00.attribute("alignment").toStdString().data())); diff --git a/sources/qetgraphicsitem/dynamicelementtextitem.cpp b/sources/qetgraphicsitem/dynamicelementtextitem.cpp index f6854b8e4..513d9fc88 100644 --- a/sources/qetgraphicsitem/dynamicelementtextitem.cpp +++ b/sources/qetgraphicsitem/dynamicelementtextitem.cpp @@ -168,7 +168,7 @@ void DynamicElementTextItem::fromXml(const QDomElement &dom_elmt) if (dom_elmt.hasAttribute("font")) { QFont font; - font.fromString(dom_elmt.attribute("font")); + QETUtils::fontFromString(font, dom_elmt.attribute("font")); setFont(font); } else //Retrocompatibility during the 0.7 dev because the font property was added lately. TODO remove this part in futur diff --git a/sources/qetgraphicsitem/independenttextitem.cpp b/sources/qetgraphicsitem/independenttextitem.cpp index 972138126..ba92533b8 100644 --- a/sources/qetgraphicsitem/independenttextitem.cpp +++ b/sources/qetgraphicsitem/independenttextitem.cpp @@ -65,7 +65,7 @@ void IndependentTextItem::fromXml(const QDomElement &e) { if (e.hasAttribute("font")) { QFont font; - font.fromString(e.attribute("font")); + QETUtils::fontFromString(font, e.attribute("font")); setFont(font); } } diff --git a/sources/qetxml.cpp b/sources/qetxml.cpp index 440dece91..042d13ae7 100644 --- a/sources/qetxml.cpp +++ b/sources/qetxml.cpp @@ -544,7 +544,7 @@ void QETXML::modelHeaderDataFromXml( else if (role_ == Qt::FontRole) { QFont font; - font.fromString(text_); + QETUtils::fontFromString(font, text_); data_ = font; } else if (role_ == Qt::TextAlignmentRole) diff --git a/sources/ui/configpage/generalconfigurationpage.cpp b/sources/ui/configpage/generalconfigurationpage.cpp index 9698fc889..000b1c547 100644 --- a/sources/ui/configpage/generalconfigurationpage.cpp +++ b/sources/ui/configpage/generalconfigurationpage.cpp @@ -101,7 +101,7 @@ GeneralConfigurationPage::GeneralConfigurationPage(QWidget *parent) : if (settings.contains("diagrameditor/dynamic_text_font")) { QFont font; - font.fromString(settings.value("diagrameditor/dynamic_text_font").toString()); + QETUtils::fontFromString(font, settings.value("diagrameditor/dynamic_text_font").toString()); QString fontInfos = font.family() + " " + QString::number(font.pointSize()) + " (" + @@ -114,7 +114,7 @@ GeneralConfigurationPage::GeneralConfigurationPage(QWidget *parent) : if (settings.contains("diagrameditor/independent_text_font")) { QFont font; - font.fromString(settings.value("diagrameditor/independent_text_font").toString()); + QETUtils::fontFromString(font, settings.value("diagrameditor/independent_text_font").toString()); QString fontInfos = font.family() + " " + QString::number(font.pointSize()) + " (" + @@ -456,7 +456,7 @@ void GeneralConfigurationPage::on_m_dyn_text_font_pb_clicked() bool ok; QSettings settings; QFont curFont; - curFont.fromString(settings.value("diagrameditor/dynamic_text_font", "Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular").toString()); + QETUtils::fontFromString(curFont, settings.value("diagrameditor/dynamic_text_font", "Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular").toString()); QFont font = QFontDialog::getFont(&ok, curFont, this); if (ok) { @@ -560,7 +560,7 @@ void GeneralConfigurationPage::on_m_indi_text_font_pb_clicked() bool ok; QSettings settings; QFont curFont; - curFont.fromString(settings.value("diagrameditor/independent_text_font", "Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular").toString()); + QETUtils::fontFromString(curFont, settings.value("diagrameditor/independent_text_font", "Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular").toString()); QFont font = QFontDialog::getFont(&ok, curFont, this); if (ok) { diff --git a/sources/utils/qetutils.cpp b/sources/utils/qetutils.cpp index 8b9c126dc..8cf457a67 100644 --- a/sources/utils/qetutils.cpp +++ b/sources/utils/qetutils.cpp @@ -151,6 +151,32 @@ void QETUtils::pixelSizedFont(QFont &font) font.setPixelSize(qRound(px)); } +namespace +{ + /** + * Legacy (Qt 5) weight <- OpenType weight, closest match, + * same table Qt uses when parsing a 10/11 field string. + */ + int legacyFontWeight(const int opentype_weight) + { + static const int weight_map[9][2] = { + {0, 100}, {12, 200}, {25, 300}, {50, 400}, {57, 500}, + {63, 600}, {75, 700}, {81, 800}, {87, 900} + }; + int legacy_weight = weight_map[0][0]; + int closest_diff = qAbs(opentype_weight - weight_map[0][1]); + for (int i = 1 ; i < 9 ; ++i) + { + const int diff = qAbs(opentype_weight - weight_map[i][1]); + if (diff < closest_diff) { + closest_diff = diff; + legacy_weight = weight_map[i][0]; + } + } + return legacy_weight; + } +} + /** * @brief QETUtils::fontToString * Serialize a font to the 10/11 field description format written by Qt 5, @@ -171,23 +197,7 @@ QString QETUtils::fontToString(const QFont &font) #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) return font.toString(); #else - //Legacy (Qt 5) weight <- OpenType weight, closest match, - //same table Qt uses when parsing a 10/11 field string. - static const int weight_map[9][2] = { - {0, 100}, {12, 200}, {25, 300}, {50, 400}, {57, 500}, - {63, 600}, {75, 700}, {81, 800}, {87, 900} - }; - const int weight = font.weight(); - int legacy_weight = weight_map[0][0]; - int closest_diff = qAbs(weight - weight_map[0][1]); - for (int i = 1 ; i < 9 ; ++i) - { - const int diff = qAbs(weight - weight_map[i][1]); - if (diff < closest_diff) { - closest_diff = diff; - legacy_weight = weight_map[i][0]; - } - } + const int legacy_weight = legacyFontWeight(font.weight()); const QChar comma(QLatin1Char(',')); QString description = font.family() + comma + @@ -206,3 +216,75 @@ QString QETUtils::fontToString(const QFont &font) return description; #endif } + +/** + * @brief QETUtils::fontFromString + * Restore a font from a description string, tolerating descriptions written + * by any Qt version. QFont::fromString() of Qt 5.x and Qt <= 6.10 rejects the + * >= 19 field format QFont::toString() emits since Qt 6.11 (16 base fields + * plus style name, font-feature count and variable-axis count), leaving a + * broken font behind. When the native parser fails, salvage such a + * description by re-composing the 10/11 field form (OpenType weight mapped + * back to the legacy scale) and parsing that instead, so no font information + * stored in existing files is lost. + * See @link https://github.com/qelectrotech/qelectrotech-source-mirror/issues/553 @endlink + * @param font : font to restore into; left untouched on failure, so the + * caller's default keeps applying. + * @param description : font description string + * @return true if the description could be parsed + */ +bool QETUtils::fontFromString(QFont &font, const QString &description) +{ + QFont parsed(font); + if (parsed.fromString(description)) { + font = parsed; + return true; + } + + const QStringList l = description.split(QLatin1Char(',')); + const int count = l.size(); + const QChar comma(QLatin1Char(',')); + + //16 base fields + style name + feature count + axis count; a family + //name containing commas adds leading fields. Descriptions actually + //using font features or variable axes append value groups whose + //layout we cannot anchor reliably, so only salvage the plain + //"...,0,0" case (the only one QET-edited files can contain). + if (count >= 19 + && l.at(count - 1).trimmed() == QLatin1String("0") + && l.at(count - 2).trimmed() == QLatin1String("0")) + { + QString legacy = QStringList(l.mid(0, count - 18)).join(comma) + comma + // family + l.at(count - 18) + comma + // pointSizeF + l.at(count - 17) + comma + // pixelSize + l.at(count - 16) + comma + // styleHint + QString::number(legacyFontWeight(l.at(count - 15).toInt())) + comma + + l.at(count - 14) + comma + // style + l.at(count - 13) + comma + // underline + l.at(count - 12) + comma + // strikeOut + l.at(count - 11) + comma + // fixedPitch + QString::number(0); + const QString style_name = l.at(count - 3); + if (!style_name.isEmpty()) { + legacy += comma + style_name; + } + + if (parsed.fromString(legacy)) { + font = parsed; + return true; + } + return false; + } + + //Some historical builds double-serialized fonts, embedding a complete + //legacy description as the family name of a second one (21 fields: + //"Caladea,9,-1,5,75,1,0,0,0,0,Bold Italic,9,-1,0,50,0,0,0,0,0,Regular"). + //The embedded leading description carries the real font, and taking it + //matches what the lenient parser of Qt 6.11+ resolves such strings to. + if (count > 11 + && parsed.fromString(QStringList(l.mid(0, 11)).join(comma))) { + font = parsed; + return true; + } + return false; +} diff --git a/sources/utils/qetutils.h b/sources/utils/qetutils.h index 5f5bc63e0..81872ce41 100644 --- a/sources/utils/qetutils.h +++ b/sources/utils/qetutils.h @@ -33,6 +33,7 @@ namespace QETUtils qreal graphicsHandlerSize(QGraphicsItem *item); void pixelSizedFont (QFont &font); QString fontToString (const QFont &font); + bool fontFromString (QFont &font, const QString &description); bool sortBeginIntString(const QString &str_a, const QString &str_b);