From e1cb6006c63a765d44249d32939b86a183fd586f Mon Sep 17 00:00:00 2001 From: Beat Hangartner Date: Fri, 9 Oct 2026 13:42:46 +0200 Subject: [PATCH] Load a picture without a usable size at size 1, not 0 DiagramImageItem::fromXml() read the size attribute with a plain toDouble(). A picture whose size was missing or not a number got size 0, so it was invisible on the folio, left out of the SVG export, and a resave wrote size="0" back, which then stayed 0 on every load. "nan" was kept and written back as such. The child, which newer files carry, already defaults its scale to 1. Read the size with an ok flag and use 1, the size a picture is added with, when it is missing, not a number, not finite, or 0. No file-format change for usable sizes. A picture that an earlier save left at size="0" shows again, at size 1, after this change. Tests: tst_imagemissingsize runs the binary's --resave and --export-svg on a fixture with six small pictures: size missing, "0", "nan", "big", "0.5" and "2". The first four fail on master (saved as 0, 0, nan, 0) and the export draws only 3 of the 6 pictures there. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Beat Hangartner --- sources/qetgraphicsitem/diagramimageitem.cpp | 7 +- tests/qttest/CMakeLists.txt | 13 +++ tests/qttest/fixtures/image_missing_size.qet | 45 ++++++++ tests/qttest/tst_imagemissingsize.cpp | 108 +++++++++++++++++++ 4 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 tests/qttest/fixtures/image_missing_size.qet create mode 100644 tests/qttest/tst_imagemissingsize.cpp diff --git a/sources/qetgraphicsitem/diagramimageitem.cpp b/sources/qetgraphicsitem/diagramimageitem.cpp index ed81b4619..843657b54 100644 --- a/sources/qetgraphicsitem/diagramimageitem.cpp +++ b/sources/qetgraphicsitem/diagramimageitem.cpp @@ -1686,7 +1686,12 @@ bool DiagramImageItem::fromXml(const QDomElement &e) // pivot), the same two-tier fallback QetShapeItem's own fromXml() // already uses for its identical element. m_transform.rotation = e.attribute("rotation").toDouble(); - m_transform.scaleX = e.attribute("size").toDouble(); + //A missing, unreadable, non-finite or zero size would scale the + //picture to nothing (and a resave wrote size="0" back): use 1, + //the size a picture is added with, as the branch does + bool size_ok = false; + const qreal size = e.attribute("size").toDouble(&size_ok); + m_transform.scaleX = (size_ok && qIsFinite(size) && size != 0) ? size : 1; m_transform.scaleY = m_transform.scaleX; m_transform.pivot = imageRect().center(); m_pivotIsCustom = false; diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 8653c86a4..ca03bc8bf 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -1024,6 +1024,19 @@ target_compile_definitions(tst_terminaluuids PRIVATE "QET_TEST_BINARY_PATH=\"$\"" "QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"") +# A picture whose size attribute is missing, unreadable, "nan" or 0 is +# loaded at size 1, not 0: the real binary's --resave and --export-svg on +# fixtures/image_missing_size.qet. +add_executable( + tst_imagemissingsize + tst_imagemissingsize.cpp) +add_test(NAME tst_imagemissingsize COMMAND tst_imagemissingsize) +set_tests_properties(tst_imagemissingsize PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") +add_dependencies(tst_imagemissingsize qelectrotech) +target_link_libraries(tst_imagemissingsize PRIVATE Qt::Test Qt::Xml) +target_compile_definitions(tst_imagemissingsize PRIVATE + "QET_TEST_BINARY_PATH=\"$\"") + # Sequential numbers saved the old way, as sequ_1/.../seqhf_1 attributes, # are all read: the real binary's --resave on # fixtures/legacy_sequential_attributes.qet, whose element and conductor diff --git a/tests/qttest/fixtures/image_missing_size.qet b/tests/qttest/fixtures/image_missing_size.qet new file mode 100644 index 000000000..af6933d73 --- /dev/null +++ b/tests/qttest/fixtures/image_missing_size.qet @@ -0,0 +1,45 @@ + + + 10/2/26 + 02-10-2026 + 2026-10-02 + image_missing_size + 18:39 + + + + + + + + + + + + + + + + + + + + + + + iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAAFUlEQVR4nGM4ISdHNmIY1TyqmRACAJaAyyGTMvplAAAAAElFTkSuQmCC + iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAAFUlEQVR4nGM4ISdHNmIY1TyqmRACAJaAyyGTMvplAAAAAElFTkSuQmCC + iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAAFUlEQVR4nGM4ISdHNmIY1TyqmRACAJaAyyGTMvplAAAAAElFTkSuQmCC + iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAAFUlEQVR4nGM4ISdHNmIY1TyqmRACAJaAyyGTMvplAAAAAElFTkSuQmCC + iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAAFUlEQVR4nGM4ISdHNmIY1TyqmRACAJaAyyGTMvplAAAAAElFTkSuQmCC + iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAAFUlEQVR4nGM4ISdHNmIY1TyqmRACAJaAyyGTMvplAAAAAElFTkSuQmCC + + + + + + Imported elements + + + + diff --git a/tests/qttest/tst_imagemissingsize.cpp b/tests/qttest/tst_imagemissingsize.cpp new file mode 100644 index 000000000..0c1534498 --- /dev/null +++ b/tests/qttest/tst_imagemissingsize.cpp @@ -0,0 +1,108 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include +#include +#include +#include +#include + +/** + A picture on a folio (, without a child) whose size + attribute is missing, unreadable, "nan" or 0 is loaded at size 1. It + used to be loaded at size 0, so it was invisible, not exported, and a + resave wrote size="0" back. Runs the real binary's --resave and + --export-svg on fixtures/image_missing_size.qet: six 20x10 pictures, + one per size value, told apart by their y position. +*/ +class tst_imagemissingsize : public QObject +{ + Q_OBJECT + + QTemporaryDir m_dir; + QString m_fixture; + QDomDocument m_resaved; + + static bool runQet(const QStringList &arguments) + { + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen")); + QProcess proc; + proc.setProcessEnvironment(env); + proc.start(QStringLiteral(QET_TEST_BINARY_PATH), arguments); + return proc.waitForFinished(60000) + && proc.exitStatus() == QProcess::NormalExit + && proc.exitCode() == 0; + } + +private slots: + void initTestCase() + { + QVERIFY2(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)), "qelectrotech binary not found"); + QVERIFY(m_dir.isValid()); + m_fixture = QFINDTESTDATA("fixtures/image_missing_size.qet"); + QVERIFY2(!m_fixture.isEmpty(), "fixture project not found"); + const QString out = m_dir.filePath(QStringLiteral("resaved.qet")); + QVERIFY2(runQet({QStringLiteral("--resave"), m_fixture, out}), "--resave failed"); + QFile file(out); + QVERIFY(file.open(QIODevice::ReadOnly)); + QVERIFY(m_resaved.setContent(&file)); + } + + void savedSize_data() + { + QTest::addColumn("y"); + QTest::addColumn("size"); + QTest::newRow("missing") << "100" << "1"; + QTest::newRow("0") << "160" << "1"; + QTest::newRow("nan") << "220" << "1"; + QTest::newRow("not a number") << "280" << "1"; + // Usable sizes are kept, below and above 1. + QTest::newRow("0.5") << "340" << "0.5"; + QTest::newRow("2") << "400" << "2"; + } + + void savedSize() + { + QFETCH(QString, y); + QFETCH(QString, size); + const QDomNodeList list = m_resaved.elementsByTagName(QStringLiteral("image")); + QDomElement found; + for (int i = 0; i < list.size(); ++i) { + const QDomElement e = list.at(i).toElement(); + if (e.attribute(QStringLiteral("y")) == y) found = e; + } + QVERIFY2(!found.isNull(), "picture not found in the resaved project"); + QCOMPARE(found.attribute(QStringLiteral("size")), size); + } + + // All six pictures are drawn in the export. + void allPicturesAreExported() + { + const QString dir = m_dir.filePath(QStringLiteral("svg")); + QVERIFY(QDir().mkpath(dir)); + QVERIFY2(runQet({QStringLiteral("--export-svg"), m_fixture, dir}), "--export-svg failed"); + const QStringList files = QDir(dir).entryList({QStringLiteral("*.svg")}, QDir::Files); + QCOMPARE(files.size(), 1); + QFile svg(QDir(dir).filePath(files.first())); + QVERIFY(svg.open(QIODevice::ReadOnly)); + QCOMPARE(QString::fromUtf8(svg.readAll()).count(QStringLiteral("