mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-03 10:34:14 +02:00
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.
This commit is contained in:
@@ -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")) {
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "../editor/graphicspart/partline.h"
|
||||
#include "../qetapp.h"
|
||||
#include "../qetversion.h"
|
||||
#include "../utils/qetutils.h"
|
||||
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QDomElement>
|
||||
@@ -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"));
|
||||
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
+3
-2
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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<Qt::Alignment>();
|
||||
m_index_0_0_data.insert(Qt::TextAlignmentRole, me.keyToValue(index_00.attribute("alignment").toStdString().data()));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
+99
-17
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user