From 5da1fe5fc8463c993155c9247a1cd8a53dd9b3d1 Mon Sep 17 00:00:00 2001 From: Dieter Mayer Date: Thu, 30 Jul 2026 21:45:23 +0200 Subject: [PATCH 1/2] Write font descriptions in the stable Qt 5 10/11-field format QFont::toString() is not stable across Qt versions: Qt 6.11 switched to a 19-field format carrying OpenType weights, which QFont::fromString() of Qt 5.x and Qt <= 6.10 rejects, leaving a broken font. Projects saved by a Qt 6.11+ build were therefore unreadable by older builds. Add QETUtils::fontToString() composing the legacy 10/11-field description (weight mapped back to the legacy scale with the same closest-match table Qt uses when parsing) and use it at every site that stores a font description in a project, element, table config or settings file. Every Qt version from 5.15 through 6.12-beta parses this form correctly, so files stay readable by every QET build in circulation. See issue #553. --- .../graphicspart/partdynamictextfield.cpp | 3 +- sources/editor/graphicspart/parttext.cpp | 3 +- sources/factory/ui/addtabledialog.cpp | 4 +- sources/properties/terminaldata.cpp | 4 +- .../ViewItem/projectdbmodel.cpp | 3 +- .../dynamicelementtextitem.cpp | 3 +- .../qetgraphicsitem/independenttextitem.cpp | 3 +- sources/qetxml.cpp | 3 +- .../configpage/generalconfigurationpage.cpp | 5 +- sources/utils/qetutils.cpp | 56 +++++++++++++++++++ sources/utils/qetutils.h | 1 + 11 files changed, 77 insertions(+), 11 deletions(-) diff --git a/sources/editor/graphicspart/partdynamictextfield.cpp b/sources/editor/graphicspart/partdynamictextfield.cpp index ee9b0c647..eea11ae89 100644 --- a/sources/editor/graphicspart/partdynamictextfield.cpp +++ b/sources/editor/graphicspart/partdynamictextfield.cpp @@ -20,6 +20,7 @@ #include "../../QPropertyUndoCommand/qpropertyundocommand.h" #include "../../qetapp.h" #include "../elementscene.h" +#include "../../utils/qetutils.h" #include #include @@ -142,7 +143,7 @@ const QDomElement PartDynamicTextField::toXml(QDomDocument &dom_doc) const root_element.setAttribute("y", QString::number(y)); root_element.setAttribute("z", QString::number(zValue())); root_element.setAttribute("rotation", QString::number(QET::correctAngle(rot))); - root_element.setAttribute("font", font().toString()); + root_element.setAttribute("font", QETUtils::fontToString(font())); root_element.setAttribute("uuid", m_uuid.toString()); root_element.setAttribute("frame", m_frame? "true" : "false"); root_element.setAttribute("text_width", QString::number(m_text_width)); diff --git a/sources/editor/graphicspart/parttext.cpp b/sources/editor/graphicspart/parttext.cpp index 25e02f1c4..3b6cc8e6e 100644 --- a/sources/editor/graphicspart/parttext.cpp +++ b/sources/editor/graphicspart/parttext.cpp @@ -23,6 +23,7 @@ #include "../elementprimitivedecorator.h" #include "../elementscene.h" #include "../ui/texteditor.h" +#include "../../utils/qetutils.h" /** Constructeur @@ -165,7 +166,7 @@ const QDomElement PartText::toXml(QDomDocument &xml_document) const xml_element.setAttribute("x", QString::number(x)); xml_element.setAttribute("y", QString::number(y)); xml_element.setAttribute("text", toPlainText()); - xml_element.setAttribute("font", font().toString()); + xml_element.setAttribute("font", QETUtils::fontToString(font())); xml_element.setAttribute("rotation", QString::number(rot)); xml_element.setAttribute("color", defaultTextColor().name()); diff --git a/sources/factory/ui/addtabledialog.cpp b/sources/factory/ui/addtabledialog.cpp index dce18168d..325b6c758 100644 --- a/sources/factory/ui/addtabledialog.cpp +++ b/sources/factory/ui/addtabledialog.cpp @@ -221,12 +221,12 @@ void AddTableDialog::saveConfig() header_object.insert("margins", QETUtils::marginsToString(this->headerMargins())); auto me = QMetaEnum::fromType(); header_object.insert("alignment", me.valueToKey(int(this->headerAlignment()))); - header_object.insert("font", this->headerFont().toString()); + header_object.insert("font", QETUtils::fontToString(this->headerFont())); QJsonObject table_object; table_object.insert("margins", QETUtils::marginsToString(this->tableMargins())); table_object.insert("alignment", me.valueToKey(int(this->tableAlignment()))); - table_object.insert("font", this->tableFont().toString()); + table_object.insert("font", QETUtils::fontToString(this->tableFont())); QJsonObject config_object; config_object.insert("header", header_object); diff --git a/sources/properties/terminaldata.cpp b/sources/properties/terminaldata.cpp index 57b135421..d06babc5c 100644 --- a/sources/properties/terminaldata.cpp +++ b/sources/properties/terminaldata.cpp @@ -17,6 +17,8 @@ */ #include "terminaldata.h" +#include "../utils/qetutils.h" + #include #include @@ -115,7 +117,7 @@ QDomElement TerminalData::toXml(QDomDocument &xml_document) const xml_element.setAttribute("show_name", "true"); xml_element.setAttribute("label_x", QString::number(m_label_pos.x())); xml_element.setAttribute("label_y", QString::number(m_label_pos.y())); - xml_element.setAttribute("label_font", m_label_font.toString()); + xml_element.setAttribute("label_font", QETUtils::fontToString(m_label_font)); xml_element.setAttribute("label_rotation", QString::number(m_label_rotation)); xml_element.setAttribute("label_halign", static_cast(m_label_halignment)); xml_element.setAttribute("label_valign", static_cast(m_label_valignment)); diff --git a/sources/qetgraphicsitem/ViewItem/projectdbmodel.cpp b/sources/qetgraphicsitem/ViewItem/projectdbmodel.cpp index 91f137379..b01f73a17 100644 --- a/sources/qetgraphicsitem/ViewItem/projectdbmodel.cpp +++ b/sources/qetgraphicsitem/ViewItem/projectdbmodel.cpp @@ -22,6 +22,7 @@ #include "../../qetinformation.h" #include "../../qetproject.h" #include "../../qetxml.h" +#include "../../utils/qetutils.h" #include #include @@ -252,7 +253,7 @@ QDomElement ProjectDBModel::toXml(QDomDocument &document) const //Add index 0,0 data auto index_00 = document.createElement("index00"); - index_00.setAttribute("font", m_index_0_0_data.value(Qt::FontRole).toString()); + index_00.setAttribute("font", QETUtils::fontToString(m_index_0_0_data.value(Qt::FontRole).value())); auto me = QMetaEnum::fromType(); index_00.setAttribute("alignment", me.valueToKey(m_index_0_0_data.value(Qt::TextAlignmentRole).toInt())); dom_element.appendChild(index_00); diff --git a/sources/qetgraphicsitem/dynamicelementtextitem.cpp b/sources/qetgraphicsitem/dynamicelementtextitem.cpp index de47ca808..f6854b8e4 100644 --- a/sources/qetgraphicsitem/dynamicelementtextitem.cpp +++ b/sources/qetgraphicsitem/dynamicelementtextitem.cpp @@ -23,6 +23,7 @@ #include "../qetgraphicsitem/conductor.h" #include "../qetgraphicsitem/terminal.h" #include "../qetinformation.h" +#include "../utils/qetutils.h" #include "crossrefitem.h" #include "element.h" #include "elementtextitemgroup.h" @@ -96,7 +97,7 @@ QDomElement DynamicElementTextItem::toXml(QDomDocument &dom_doc) const root_element.setAttribute("uuid", m_uuid.toString()); root_element.setAttribute("frame", m_frame? "true" : "false"); root_element.setAttribute("text_width", QString::number(m_text_width)); - root_element.setAttribute("font", font().toString()); + root_element.setAttribute("font", QETUtils::fontToString(font())); root_element.setAttribute("keep_visual_rotation", m_keep_visual_rotation ? "true" : "false"); QMetaEnum me = textFromMetaEnum(); diff --git a/sources/qetgraphicsitem/independenttextitem.cpp b/sources/qetgraphicsitem/independenttextitem.cpp index 1fc4b8559..972138126 100644 --- a/sources/qetgraphicsitem/independenttextitem.cpp +++ b/sources/qetgraphicsitem/independenttextitem.cpp @@ -21,6 +21,7 @@ #include "../diagramcommands.h" #include "../qet.h" #include "../qetapp.h" +#include "../utils/qetutils.h" #include #include @@ -80,7 +81,7 @@ QDomElement IndependentTextItem::toXml(QDomDocument &document) const result.setAttribute("y", QString("%1").arg(pos().y())); result.setAttribute("text", toHtml()); result.setAttribute("rotation", QString::number(QET::correctAngle(rotation()))); - result.setAttribute("font", font().toString()); + result.setAttribute("font", QETUtils::fontToString(font())); return(result); } diff --git a/sources/qetxml.cpp b/sources/qetxml.cpp index 92db3f787..440dece91 100644 --- a/sources/qetxml.cpp +++ b/sources/qetxml.cpp @@ -18,6 +18,7 @@ #include "qetxml.h" #include "NameList/nameslist.h" +#include "utils/qetutils.h" #include #include @@ -477,7 +478,7 @@ QDomElement QETXML::modelHeaderDataToXml( else if (role == Qt::FontRole) { auto font = variant.value(); - text_node.setData(font.toString()); + text_node.setData(QETUtils::fontToString(font)); } else if (role == Qt::TextAlignmentRole) { diff --git a/sources/ui/configpage/generalconfigurationpage.cpp b/sources/ui/configpage/generalconfigurationpage.cpp index d639e3a6a..9698fc889 100644 --- a/sources/ui/configpage/generalconfigurationpage.cpp +++ b/sources/ui/configpage/generalconfigurationpage.cpp @@ -21,6 +21,7 @@ #include "../../qeticons.h" #include "ui_generalconfigurationpage.h" #include "../../utils/qetsettings.h" +#include "../../utils/qetutils.h" #include "../../qetmessagebox.h" #include #include @@ -459,7 +460,7 @@ void GeneralConfigurationPage::on_m_dyn_text_font_pb_clicked() QFont font = QFontDialog::getFont(&ok, curFont, this); if (ok) { - settings.setValue("diagrameditor/dynamic_text_font", font.toString()); + settings.setValue("diagrameditor/dynamic_text_font", QETUtils::fontToString(font)); QString fontInfos = font.family() + " " + QString::number(font.pointSize()) + " (" + font.styleName() + ")"; @@ -563,7 +564,7 @@ void GeneralConfigurationPage::on_m_indi_text_font_pb_clicked() QFont font = QFontDialog::getFont(&ok, curFont, this); if (ok) { - settings.setValue("diagrameditor/independent_text_font", font.toString()); + settings.setValue("diagrameditor/independent_text_font", QETUtils::fontToString(font)); QString fontInfos = font.family() + " " + QString::number(font.pointSize()) + " (" + font.styleName() + ")"; diff --git a/sources/utils/qetutils.cpp b/sources/utils/qetutils.cpp index 4f6a393f7..8b9c126dc 100644 --- a/sources/utils/qetutils.cpp +++ b/sources/utils/qetutils.cpp @@ -150,3 +150,59 @@ void QETUtils::pixelSizedFont(QFont &font) auto px = font.pointSizeF()/72 * QFontMetrics{font}.fontDpi(); font.setPixelSize(qRound(px)); } + +/** + * @brief QETUtils::fontToString + * Serialize a font to the 10/11 field description format written by Qt 5, + * to be used instead of QFont::toString() everywhere a font description is + * stored in a project, element or settings file. + * The format of QFont::toString() is not stable across Qt versions : Qt 6.11 + * switched to a 19 field format carrying OpenType weights, which + * QFont::fromString() of Qt 5.x and Qt <= 6.10 rejects, leaving a broken font. + * The 10/11 field form is parsed correctly by every Qt version (they convert + * the legacy weight scale as needed), so composing it ourselves keeps files + * readable by every QET build in circulation. + * See @link https://github.com/qelectrotech/qelectrotech-source-mirror/issues/553 @endlink + * @param font + * @return the font description string + */ +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 QChar comma(QLatin1Char(',')); + QString description = font.family() + comma + + QString::number(font.pointSizeF()) + comma + + QString::number(font.pixelSize()) + comma + + QString::number(int(font.styleHint())) + comma + + QString::number(legacy_weight) + comma + + QString::number(int(font.style())) + comma + + QString::number(int(font.underline())) + comma + + QString::number(int(font.strikeOut())) + comma + + QString::number(int(font.fixedPitch())) + comma + + QString::number(0); + if (!font.styleName().isEmpty()) { + description += QChar(',') + font.styleName(); + } + return description; +#endif +} diff --git a/sources/utils/qetutils.h b/sources/utils/qetutils.h index ffc003db2..5f5bc63e0 100644 --- a/sources/utils/qetutils.h +++ b/sources/utils/qetutils.h @@ -32,6 +32,7 @@ namespace QETUtils QMargins marginsFromString(const QString &string); qreal graphicsHandlerSize(QGraphicsItem *item); void pixelSizedFont (QFont &font); + QString fontToString (const QFont &font); bool sortBeginIntString(const QString &str_a, const QString &str_b); From 68ffacc9e6a1cff5f51d4ba5fd6e747fa03a6188 Mon Sep 17 00:00:00 2001 From: Dieter Mayer Date: Fri, 31 Jul 2026 07:58:54 +0200 Subject: [PATCH 2/2] Salvage font descriptions written by Qt 6.11+ when parsing fails QFont::fromString() of Qt 5.x and Qt <= 6.10 rejects the >= 19 field descriptions QFont::toString() emits since Qt 6.11, silently leaving a broken font at every read site. Add QETUtils::fontFromString(): try the native parser first, and on failure re-compose the legacy 10/11 field form from the known Qt 6.11 field layout (OpenType weight mapped back to the legacy scale) so no font information stored in existing files is lost. Also salvage the 21 field double-serialized descriptions left behind by some historical builds (a complete legacy description embedded as the family name of a second one) by taking the embedded leading description, matching what the lenient parser of Qt 6.11+ resolves them to. All font read sites now go through the helper; on failure the default font of the caller is left untouched instead of a cleared family. Verified end to end on a Qt 5.15 build: a project whose 53 font attributes were rewritten into the 19 field Qt 6.11 format loads and autosaves byte-identical to the original legacy file (family, sizes, bold/italic/underline, style name all preserved), and a mixed file containing the exact 21 field string from the issue comes back normalized as "Caladea,9,-1,5,75,1,0,0,0,0,Bold Italic". See issue #553. --- .../graphicspart/partdynamictextfield.cpp | 2 +- sources/editor/graphicspart/parttext.cpp | 2 +- sources/factory/elementpicturefactory.cpp | 3 +- sources/factory/ui/addtabledialog.cpp | 4 +- sources/properties/terminaldata.cpp | 2 +- sources/qetapp.cpp | 5 +- .../ViewItem/projectdbmodel.cpp | 2 +- .../dynamicelementtextitem.cpp | 2 +- .../qetgraphicsitem/independenttextitem.cpp | 2 +- sources/qetxml.cpp | 2 +- .../configpage/generalconfigurationpage.cpp | 8 +- sources/utils/qetutils.cpp | 116 +++++++++++++++--- sources/utils/qetutils.h | 1 + 13 files changed, 118 insertions(+), 33 deletions(-) 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);