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.
This commit is contained in:
Dieter Mayer
2026-07-30 21:45:23 +02:00
parent 120ca08209
commit 5da1fe5fc8
11 changed files with 77 additions and 11 deletions
@@ -20,6 +20,7 @@
#include "../../QPropertyUndoCommand/qpropertyundocommand.h" #include "../../QPropertyUndoCommand/qpropertyundocommand.h"
#include "../../qetapp.h" #include "../../qetapp.h"
#include "../elementscene.h" #include "../elementscene.h"
#include "../../utils/qetutils.h"
#include <QApplication> #include <QApplication>
#include <QColor> #include <QColor>
@@ -142,7 +143,7 @@ const QDomElement PartDynamicTextField::toXml(QDomDocument &dom_doc) const
root_element.setAttribute("y", QString::number(y)); root_element.setAttribute("y", QString::number(y));
root_element.setAttribute("z", QString::number(zValue())); root_element.setAttribute("z", QString::number(zValue()));
root_element.setAttribute("rotation", QString::number(QET::correctAngle(rot))); 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("uuid", m_uuid.toString());
root_element.setAttribute("frame", m_frame? "true" : "false"); root_element.setAttribute("frame", m_frame? "true" : "false");
root_element.setAttribute("text_width", QString::number(m_text_width)); root_element.setAttribute("text_width", QString::number(m_text_width));
+2 -1
View File
@@ -23,6 +23,7 @@
#include "../elementprimitivedecorator.h" #include "../elementprimitivedecorator.h"
#include "../elementscene.h" #include "../elementscene.h"
#include "../ui/texteditor.h" #include "../ui/texteditor.h"
#include "../../utils/qetutils.h"
/** /**
Constructeur Constructeur
@@ -165,7 +166,7 @@ const QDomElement PartText::toXml(QDomDocument &xml_document) const
xml_element.setAttribute("x", QString::number(x)); xml_element.setAttribute("x", QString::number(x));
xml_element.setAttribute("y", QString::number(y)); xml_element.setAttribute("y", QString::number(y));
xml_element.setAttribute("text", toPlainText()); 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("rotation", QString::number(rot));
xml_element.setAttribute("color", defaultTextColor().name()); xml_element.setAttribute("color", defaultTextColor().name());
+2 -2
View File
@@ -221,12 +221,12 @@ void AddTableDialog::saveConfig()
header_object.insert("margins", QETUtils::marginsToString(this->headerMargins())); header_object.insert("margins", QETUtils::marginsToString(this->headerMargins()));
auto me = QMetaEnum::fromType<Qt::Alignment>(); auto me = QMetaEnum::fromType<Qt::Alignment>();
header_object.insert("alignment", me.valueToKey(int(this->headerAlignment()))); 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; QJsonObject table_object;
table_object.insert("margins", QETUtils::marginsToString(this->tableMargins())); table_object.insert("margins", QETUtils::marginsToString(this->tableMargins()));
table_object.insert("alignment", me.valueToKey(int(this->tableAlignment()))); 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; QJsonObject config_object;
config_object.insert("header", header_object); config_object.insert("header", header_object);
+3 -1
View File
@@ -17,6 +17,8 @@
*/ */
#include "terminaldata.h" #include "terminaldata.h"
#include "../utils/qetutils.h"
#include <QGraphicsObject> #include <QGraphicsObject>
#include <QDebug> #include <QDebug>
@@ -115,7 +117,7 @@ QDomElement TerminalData::toXml(QDomDocument &xml_document) const
xml_element.setAttribute("show_name", "true"); xml_element.setAttribute("show_name", "true");
xml_element.setAttribute("label_x", QString::number(m_label_pos.x())); 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_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_rotation", QString::number(m_label_rotation));
xml_element.setAttribute("label_halign", static_cast<int>(m_label_halignment)); xml_element.setAttribute("label_halign", static_cast<int>(m_label_halignment));
xml_element.setAttribute("label_valign", static_cast<int>(m_label_valignment)); xml_element.setAttribute("label_valign", static_cast<int>(m_label_valignment));
@@ -22,6 +22,7 @@
#include "../../qetinformation.h" #include "../../qetinformation.h"
#include "../../qetproject.h" #include "../../qetproject.h"
#include "../../qetxml.h" #include "../../qetxml.h"
#include "../../utils/qetutils.h"
#include <QSqlError> #include <QSqlError>
#include <QSqlRecord> #include <QSqlRecord>
@@ -252,7 +253,7 @@ QDomElement ProjectDBModel::toXml(QDomDocument &document) const
//Add index 0,0 data //Add index 0,0 data
auto index_00 = document.createElement("index00"); 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<QFont>()));
auto me = QMetaEnum::fromType<Qt::Alignment>(); auto me = QMetaEnum::fromType<Qt::Alignment>();
index_00.setAttribute("alignment", me.valueToKey(m_index_0_0_data.value(Qt::TextAlignmentRole).toInt())); index_00.setAttribute("alignment", me.valueToKey(m_index_0_0_data.value(Qt::TextAlignmentRole).toInt()));
dom_element.appendChild(index_00); dom_element.appendChild(index_00);
@@ -23,6 +23,7 @@
#include "../qetgraphicsitem/conductor.h" #include "../qetgraphicsitem/conductor.h"
#include "../qetgraphicsitem/terminal.h" #include "../qetgraphicsitem/terminal.h"
#include "../qetinformation.h" #include "../qetinformation.h"
#include "../utils/qetutils.h"
#include "crossrefitem.h" #include "crossrefitem.h"
#include "element.h" #include "element.h"
#include "elementtextitemgroup.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("uuid", m_uuid.toString());
root_element.setAttribute("frame", m_frame? "true" : "false"); root_element.setAttribute("frame", m_frame? "true" : "false");
root_element.setAttribute("text_width", QString::number(m_text_width)); 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"); root_element.setAttribute("keep_visual_rotation", m_keep_visual_rotation ? "true" : "false");
QMetaEnum me = textFromMetaEnum(); QMetaEnum me = textFromMetaEnum();
@@ -21,6 +21,7 @@
#include "../diagramcommands.h" #include "../diagramcommands.h"
#include "../qet.h" #include "../qet.h"
#include "../qetapp.h" #include "../qetapp.h"
#include "../utils/qetutils.h"
#include <QDomElement> #include <QDomElement>
#include <QSettings> #include <QSettings>
@@ -80,7 +81,7 @@ QDomElement IndependentTextItem::toXml(QDomDocument &document) const
result.setAttribute("y", QString("%1").arg(pos().y())); result.setAttribute("y", QString("%1").arg(pos().y()));
result.setAttribute("text", toHtml()); result.setAttribute("text", toHtml());
result.setAttribute("rotation", QString::number(QET::correctAngle(rotation()))); result.setAttribute("rotation", QString::number(QET::correctAngle(rotation())));
result.setAttribute("font", font().toString()); result.setAttribute("font", QETUtils::fontToString(font()));
return(result); return(result);
} }
+2 -1
View File
@@ -18,6 +18,7 @@
#include "qetxml.h" #include "qetxml.h"
#include "NameList/nameslist.h" #include "NameList/nameslist.h"
#include "utils/qetutils.h"
#include <QDir> #include <QDir>
#include <QFont> #include <QFont>
@@ -477,7 +478,7 @@ QDomElement QETXML::modelHeaderDataToXml(
else if (role == Qt::FontRole) else if (role == Qt::FontRole)
{ {
auto font = variant.value<QFont>(); auto font = variant.value<QFont>();
text_node.setData(font.toString()); text_node.setData(QETUtils::fontToString(font));
} }
else if (role == Qt::TextAlignmentRole) else if (role == Qt::TextAlignmentRole)
{ {
@@ -21,6 +21,7 @@
#include "../../qeticons.h" #include "../../qeticons.h"
#include "ui_generalconfigurationpage.h" #include "ui_generalconfigurationpage.h"
#include "../../utils/qetsettings.h" #include "../../utils/qetsettings.h"
#include "../../utils/qetutils.h"
#include "../../qetmessagebox.h" #include "../../qetmessagebox.h"
#include <QFileDialog> #include <QFileDialog>
#include <QFontDialog> #include <QFontDialog>
@@ -459,7 +460,7 @@ void GeneralConfigurationPage::on_m_dyn_text_font_pb_clicked()
QFont font = QFontDialog::getFont(&ok, curFont, this); QFont font = QFontDialog::getFont(&ok, curFont, this);
if (ok) if (ok)
{ {
settings.setValue("diagrameditor/dynamic_text_font", font.toString()); settings.setValue("diagrameditor/dynamic_text_font", QETUtils::fontToString(font));
QString fontInfos = font.family() + " " + QString fontInfos = font.family() + " " +
QString::number(font.pointSize()) + " (" + QString::number(font.pointSize()) + " (" +
font.styleName() + ")"; font.styleName() + ")";
@@ -563,7 +564,7 @@ void GeneralConfigurationPage::on_m_indi_text_font_pb_clicked()
QFont font = QFontDialog::getFont(&ok, curFont, this); QFont font = QFontDialog::getFont(&ok, curFont, this);
if (ok) if (ok)
{ {
settings.setValue("diagrameditor/independent_text_font", font.toString()); settings.setValue("diagrameditor/independent_text_font", QETUtils::fontToString(font));
QString fontInfos = font.family() + " " + QString fontInfos = font.family() + " " +
QString::number(font.pointSize()) + " (" + QString::number(font.pointSize()) + " (" +
font.styleName() + ")"; font.styleName() + ")";
+56
View File
@@ -150,3 +150,59 @@ void QETUtils::pixelSizedFont(QFont &font)
auto px = font.pointSizeF()/72 * QFontMetrics{font}.fontDpi(); auto px = font.pointSizeF()/72 * QFontMetrics{font}.fontDpi();
font.setPixelSize(qRound(px)); 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
}
+1
View File
@@ -32,6 +32,7 @@ namespace QETUtils
QMargins marginsFromString(const QString &string); QMargins marginsFromString(const QString &string);
qreal graphicsHandlerSize(QGraphicsItem *item); qreal graphicsHandlerSize(QGraphicsItem *item);
void pixelSizedFont (QFont &font); void pixelSizedFont (QFont &font);
QString fontToString (const QFont &font);
bool sortBeginIntString(const QString &str_a, const QString &str_b); bool sortBeginIntString(const QString &str_a, const QString &str_b);