diff --git a/sources/cli_export.cpp b/sources/cli_export.cpp index 80e7f1aa4..ffad87a83 100644 --- a/sources/cli_export.cpp +++ b/sources/cli_export.cpp @@ -283,7 +283,8 @@ int exportPdf(QETProject &project, const QString &output, } int exportImages(QETProject &project, const QString &format, - const QString &out_dir, bool showTerminals = false) + const QString &out_dir, bool showTerminals = false, + bool svgSmallPictures = false) { const QList diagrams = project.diagrams(); if (diagrams.isEmpty()) { @@ -306,8 +307,10 @@ int exportImages(QETProject &project, const QString &format, gen.setViewBox(QRect(0, 0, r.width(), r.height())); gen.setTitle(diagram->title()); QPainter painter(&gen); + diagram->setSmallPictures(svgSmallPictures); renderDiagram(diagram, painter, QRectF(QPointF(0, 0), r.size()), showTerminals); + diagram->setSmallPictures(false); painter.end(); } else { // png QImage image(r.size(), QImage::Format_ARGB32); @@ -1014,6 +1017,7 @@ int run(const QStringList &args) const bool showTerminals = filtered.removeAll("--show-terminals") > 0; const bool dxfBlocks = filtered.removeAll("--dxf-blocks") > 0; const bool dxfAttributes = filtered.removeAll("--dxf-attributes") > 0; + const bool svgSmallPictures = filtered.removeAll("--svg-small-pictures") > 0; // --no-slaves and --no-junctions leave rows out of --export-bom. const bool includeSlaves = filtered.removeAll("--no-slaves") == 0; const bool includeJunctions = filtered.removeAll("--no-junctions") == 0; @@ -1085,7 +1089,8 @@ int run(const QStringList &args) return resaveProject(project, output); if (format == "settb") return setTitleBlock(project, output, rest.mid(2)); - return exportImages(project, format, output, showTerminals); + return exportImages(project, format, output, showTerminals, + svgSmallPictures); } } // namespace CLIExport diff --git a/sources/cli_export.h b/sources/cli_export.h index efb3ad59e..09f66826f 100644 --- a/sources/cli_export.h +++ b/sources/cli_export.h @@ -44,7 +44,7 @@ namespace CLIExport { Usage: qelectrotech --export-pdf [--show-terminals] qelectrotech --export-png [--show-terminals] - qelectrotech --export-svg [--show-terminals] + qelectrotech --export-svg [--show-terminals] [--svg-small-pictures] qelectrotech --export-dxf [--show-terminals] [--dxf-blocks] [--dxf-attributes] qelectrotech --export-cables qelectrotech --export-wires @@ -71,6 +71,9 @@ namespace CLIExport { --dxf-attributes: as --dxf-blocks, and a symbol's texts (its label, function...) are attributes of its INSERT. LibreCAD does not show attributes. + --svg-small-pictures: in an SVG, each picture at most four times the + size it is drawn at instead of its full resolution: a much + smaller file, still sharp when printed. cables: wiring list (one row per conductor) as CSV. wires: list of distinct wire numbers as CSV. bom: bill of materials (one row per element) as CSV. diff --git a/sources/diagram.h b/sources/diagram.h index 44748c181..93b7f6bd8 100644 --- a/sources/diagram.h +++ b/sources/diagram.h @@ -133,6 +133,7 @@ class Diagram : public QGraphicsScene bool draw_terminals_; bool draw_terminal_names_; bool draw_colored_conductors_; + bool m_small_pictures = false; QString m_conductors_autonum_name; DiagramEventInterface *m_event_interface; @@ -284,6 +285,11 @@ class Diagram : public QGraphicsScene void setDrawTerminalNames(bool); bool drawColoredConductors() const; void setDrawColoredConductors(bool); + /// While true, pictures draw a copy of a few times the size + /// they are drawn at, not their full resolution (SVG export, + /// ExportProperties::svg_small_pictures) + bool smallPictures() const {return m_small_pictures;} + void setSmallPictures(bool small) {m_small_pictures = small;} QString title() const; bool toPaintDevice(QPaintDevice &, int = -1, int = -1, diff --git a/sources/exportdialog.cpp b/sources/exportdialog.cpp index 42e2f8a67..f0996e4dd 100644 --- a/sources/exportdialog.cpp +++ b/sources/exportdialog.cpp @@ -417,12 +417,14 @@ void ExportDialog::generateSvg( // genere une QPicture a partir du schema QPicture picture; + diagram -> setSmallPictures(epw -> exportProperties().svg_small_pictures); diagram -> toPaintDevice( picture, width, height, keep_aspect_ratio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio ); + diagram -> setSmallPictures(false); // "joue" la QPicture sur un QSvgGenerator QSvgGenerator svg_engine; diff --git a/sources/exportproperties.cpp b/sources/exportproperties.cpp index cd53bc806..1c4cfc404 100644 --- a/sources/exportproperties.cpp +++ b/sources/exportproperties.cpp @@ -39,6 +39,7 @@ ExportProperties::ExportProperties() : draw_colored_conductors(true), dxf_blocks(false), dxf_attributes(false), + svg_small_pictures(false), exported_area(QET::BorderArea) { } @@ -83,6 +84,8 @@ void ExportProperties::toSettings(QSettings &settings, dxf_blocks); settings.setValue(prefix % "dxfattributes", dxf_attributes); + settings.setValue(prefix % "svgsmallpictures", + svg_small_pictures); settings.setValue(prefix % "area", QET::diagramAreaToString(exported_area)); } @@ -125,6 +128,8 @@ void ExportProperties::fromSettings(QSettings &settings, false).toBool(); dxf_attributes = settings.value(prefix % "dxfattributes", false).toBool(); + svg_small_pictures = settings.value(prefix % "svgsmallpictures", + false).toBool(); exported_area = QET::diagramAreaFromString( settings.value( diff --git a/sources/exportproperties.h b/sources/exportproperties.h index c8feca461..39ca00c32 100644 --- a/sources/exportproperties.h +++ b/sources/exportproperties.h @@ -52,6 +52,7 @@ class ExportProperties { bool draw_colored_conductors; ///< Whether to render conductors colors bool dxf_blocks; ///< DXF: each symbol as a block, placed with an INSERT bool dxf_attributes; ///< DXF, with dxf_blocks: a symbol's texts as attributes of its INSERT + bool svg_small_pictures; ///< SVG: pictures at most 4x the size they are drawn, not full resolution QET::DiagramArea exported_area; ///< Area of diagrams to be rendered }; #endif diff --git a/sources/exportpropertieswidget.cpp b/sources/exportpropertieswidget.cpp index e2c9ea70b..2b248ccc5 100644 --- a/sources/exportpropertieswidget.cpp +++ b/sources/exportpropertieswidget.cpp @@ -68,6 +68,7 @@ ExportProperties ExportPropertiesWidget::exportProperties() const export_properties.draw_colored_conductors = draw_colored_conductors -> isChecked(); export_properties.dxf_blocks = dxf_blocks -> isChecked(); export_properties.dxf_attributes = dxf_attributes -> isChecked(); + export_properties.svg_small_pictures = svg_small_pictures -> isChecked(); export_properties.exported_area = export_border -> isChecked() ? QET::BorderArea : QET::ElementsArea; return(export_properties); @@ -94,6 +95,7 @@ void ExportPropertiesWidget::setExportProperties(const ExportProperties &export_ dxf_blocks -> setChecked(export_properties.dxf_blocks); dxf_attributes -> setChecked(export_properties.dxf_attributes); dxf_attributes -> setEnabled(export_properties.dxf_blocks); + svg_small_pictures -> setChecked(export_properties.svg_small_pictures); if (export_properties.exported_area == QET::BorderArea) { export_border -> setChecked(true); @@ -125,6 +127,7 @@ void ExportPropertiesWidget::setPrintingMode(bool mode) { export_border -> setVisible(!mode); export_elements -> setVisible(!mode); draw_bg_transparent -> setVisible(!mode); + svg_small_pictures -> setVisible(!mode); } /** @@ -235,6 +238,11 @@ void ExportPropertiesWidget::build() dxf_attributes -> setToolTip(tr("A symbol's texts (label, function...) become attributes of its block. LibreCAD does not show attributes.")); optionshlayout -> addWidget(dxf_attributes, 5, 1); connect(dxf_blocks, &QCheckBox::toggled, dxf_attributes, &QCheckBox::setEnabled); + + // pictures not at full resolution in an SVG: a much smaller file + svg_small_pictures = new QCheckBox(tr("SVG: smaller pictures"), groupbox_options); + svg_small_pictures -> setToolTip(tr("Pictures are stored at four times the size they have on the folio instead of their full resolution: a much smaller file, still sharp when printed")); + optionshlayout -> addWidget(svg_small_pictures, 5, 0); vboxLayout -> addWidget(groupbox_options); @@ -252,6 +260,7 @@ void ExportPropertiesWidget::build() setTabOrder(draw_colored_conductors, draw_bg_transparent); setTabOrder(draw_bg_transparent, dxf_blocks); setTabOrder(dxf_blocks, dxf_attributes); + setTabOrder(draw_bg_transparent, svg_small_pictures); // connexion du bouton permettant le choix du repertoire connect(button_browse, &QPushButton::released, this, &ExportPropertiesWidget::slot_chooseADirectory); @@ -269,6 +278,7 @@ void ExportPropertiesWidget::build() connect(draw_colored_conductors, &QCheckBox::stateChanged, this, &ExportPropertiesWidget::optionChanged); connect(dxf_blocks, &QCheckBox::stateChanged, this, &ExportPropertiesWidget::optionChanged); connect(dxf_attributes, &QCheckBox::stateChanged, this, &ExportPropertiesWidget::optionChanged); + connect(svg_small_pictures, &QCheckBox::stateChanged, this, &ExportPropertiesWidget::optionChanged); #else connect(draw_grid, &QCheckBox::checkStateChanged, this, &ExportPropertiesWidget::optionChanged); connect(draw_border, &QCheckBox::checkStateChanged, this, &ExportPropertiesWidget::optionChanged); @@ -279,5 +289,6 @@ void ExportPropertiesWidget::build() connect(draw_colored_conductors, &QCheckBox::checkStateChanged, this, &ExportPropertiesWidget::optionChanged); connect(dxf_blocks, &QCheckBox::checkStateChanged, this, &ExportPropertiesWidget::optionChanged); connect(dxf_attributes, &QCheckBox::checkStateChanged, this, &ExportPropertiesWidget::optionChanged); + connect(svg_small_pictures, &QCheckBox::checkStateChanged, this, &ExportPropertiesWidget::optionChanged); #endif } diff --git a/sources/exportpropertieswidget.h b/sources/exportpropertieswidget.h index 5b96e4273..5d850cbe6 100644 --- a/sources/exportpropertieswidget.h +++ b/sources/exportpropertieswidget.h @@ -67,6 +67,7 @@ class ExportPropertiesWidget : public QWidget { QCheckBox *draw_colored_conductors; QCheckBox *dxf_blocks; QCheckBox *dxf_attributes; + QCheckBox *svg_small_pictures; QRadioButton *export_border; QRadioButton *export_elements; QButtonGroup *exported_content_choices; diff --git a/sources/qetgraphicsitem/diagramimageitem.cpp b/sources/qetgraphicsitem/diagramimageitem.cpp index 4c5f05145..14c99b60d 100644 --- a/sources/qetgraphicsitem/diagramimageitem.cpp +++ b/sources/qetgraphicsitem/diagramimageitem.cpp @@ -167,7 +167,28 @@ void DiagramImageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * return; } - DarkImageRendering::paintPixmap(painter, pixmap_, m_adapt_to_dark_theme); + // SVG export with smaller pictures: a copy four times the size the + // picture is drawn at, when that is smaller than the picture. An + // SVG stores what it is given, so a photo shown at a tenth of its + // size would otherwise be written whole. + const QTransform world = painter -> worldTransform(); + const qreal drawn_scale = std::sqrt(std::abs(world.determinant())); + const QSize small_size = (QSizeF(pixmap_.size()) * drawn_scale * 4).toSize(); + if (diagram() && diagram() -> smallPictures() && !pixmap_.isNull() + && small_size.width() < pixmap_.width() + && small_size.height() < pixmap_.height() + && !small_size.isEmpty()) + { + const QPixmap small = pixmap_.scaled(small_size, Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + painter -> save(); + painter -> scale(qreal(pixmap_.width()) / small.width(), + qreal(pixmap_.height()) / small.height()); + DarkImageRendering::paintPixmap(painter, small, m_adapt_to_dark_theme); + painter -> restore(); + } + else + DarkImageRendering::paintPixmap(painter, pixmap_, m_adapt_to_dark_theme); Q_UNUSED(option); Q_UNUSED(widget); diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index b5be0cf96..3a7c68b0f 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -271,6 +271,15 @@ qet_add_app_test(tst_rotateselectionimage tst_rotateselectionimage.cpp) # it selected and records no undo step (#1414); the key goes to the view. qet_add_app_test(tst_imagehandleescape tst_imagehandleescape.cpp) +# SVG export with smaller pictures (--svg-small-pictures, the export dialog's +# option): a picture drawn at a tenth of its size is stored at four times +# that, a small one whole; without the option every picture is whole. +qet_add_app_test(tst_svgsmallpictures tst_svgsmallpictures.cpp) +add_dependencies(tst_svgsmallpictures qelectrotech) +target_link_libraries(tst_svgsmallpictures PRIVATE Qt::Svg) +target_compile_definitions(tst_svgsmallpictures PRIVATE + "QET_TEST_BINARY_PATH=\"$\"") + # TextResizeHandles: the corner handles that change the width of a text box, # on a plain QGraphicsTextItem with the "textWidth" property QET texts have. add_executable( diff --git a/tests/qttest/fixtures/svg_small_pictures.qet b/tests/qttest/fixtures/svg_small_pictures.qet new file mode 100644 index 000000000..c47c4d7b0 --- /dev/null +++ b/tests/qttest/fixtures/svg_small_pictures.qet @@ -0,0 +1,41 @@ + + + 10/2/26 + 02-10-2026 + 2026-10-02 + svg_small_pictures + 18:39 + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + Imported elements + + + + diff --git a/tests/qttest/tst_svgsmallpictures.cpp b/tests/qttest/tst_svgsmallpictures.cpp new file mode 100644 index 000000000..5df692832 --- /dev/null +++ b/tests/qttest/tst_svgsmallpictures.cpp @@ -0,0 +1,143 @@ +/* + 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 "qetapptest.h" +#include "qetgraphicsitem/diagramimageitem.h" + +#include +#include +#include +#include +#include +#include +#include + +/** + SVG export with "smaller pictures" (ExportProperties::svg_small_pictures, + --svg-small-pictures) stores each picture at four times the size it is + drawn at when that is smaller than the picture, and at full resolution + otherwise. fixtures/svg_small_pictures.qet has an 800x600 picture drawn + at a tenth of its size and a 40x30 one at full size. +*/ +class tst_svgsmallpictures : public QObject +{ + Q_OBJECT + + QString m_fixture; + + /// Pixel size of each picture embedded in @p svg, smallest first + static QList embeddedSizes(const QByteArray &svg) + { + static const QRegularExpression data( + QStringLiteral("data:image/png;base64,([A-Za-z0-9+/=\\s]+)")); + QList sizes; + auto it = data.globalMatch(QString::fromLatin1(svg)); + while (it.hasNext()) + sizes << QImage::fromData(QByteArray::fromBase64( + it.next().captured(1).toLatin1())).size(); + std::sort(sizes.begin(), sizes.end(), [](const QSize &a, const QSize &b) { + return a.width() < b.width(); + }); + return sizes; + } + + QByteArray exportWithCli(const QStringList &extra) + { + QTemporaryDir dir; + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen")); + QProcess proc; + proc.setProcessEnvironment(env); + proc.start(QStringLiteral(QET_TEST_BINARY_PATH), + QStringList{QStringLiteral("--export-svg"), m_fixture, dir.path()} + extra); + if (!proc.waitForFinished(60000) || proc.exitCode() != 0) + return {}; + const QStringList files = QDir(dir.path()).entryList({QStringLiteral("*.svg")}); + QFile svg(QDir(dir.path()).filePath(files.value(0))); + return svg.open(QIODevice::ReadOnly) ? svg.readAll() : QByteArray(); + } + + /// As ExportDialog::generateSvg(): the folio drawn into a QPicture, + /// played into the SVG generator + static QByteArray exportLikeTheDialog(Diagram *diagram, bool small) + { + const QSize size = diagram->imageSize(); + QPicture picture; + diagram->setSmallPictures(small); + diagram->toPaintDevice(picture, size.width(), size.height()); + diagram->setSmallPictures(false); + + QByteArray svg; + QBuffer buffer(&svg); + buffer.open(QIODevice::WriteOnly); + QSvgGenerator generator; + generator.setSize(size); + generator.setOutputDevice(&buffer); + QPainter painter(&generator); + picture.play(&painter); + painter.end(); + return svg; + } + +private slots: + void initTestCase() + { + m_fixture = QFINDTESTDATA("fixtures/svg_small_pictures.qet"); + QVERIFY2(!m_fixture.isEmpty(), "fixture project not found"); + QVERIFY(QetAppTest::editor()); + } + + void cliFullResolutionByDefault() + { + const QByteArray svg = exportWithCli({}); + QVERIFY2(!svg.isEmpty(), "--export-svg failed"); + QCOMPARE(embeddedSizes(svg), (QList{QSize(40, 30), QSize(800, 600)})); + } + + void cliSmallPictures() + { + const QByteArray svg = exportWithCli({QStringLiteral("--svg-small-pictures")}); + QVERIFY2(!svg.isEmpty(), "--export-svg --svg-small-pictures failed"); + // 800 x 600 drawn at 0.1, times 4; the 40 x 30 one is kept whole + QCOMPARE(embeddedSizes(svg), (QList{QSize(40, 30), QSize(320, 240)})); + } + + void dialogSmallPicturesThenFullAgain() + { + QFile file(m_fixture); + QVERIFY(file.open(QIODevice::ReadOnly)); + QDomDocument document; + QVERIFY(document.setContent(&file)); + const QDomElement image_xml = document.elementsByTagName( + QStringLiteral("image")).at(0).toElement(); + Diagram *diagram = QetAppTest::diagram(); + auto *picture = new DiagramImageItem(); + QVERIFY(picture->fromXml(image_xml)); + diagram->addItem(picture); + + QCOMPARE(embeddedSizes(exportLikeTheDialog(diagram, true)), + QList{QSize(320, 240)}); + // The option lasts for one export only + QVERIFY(!diagram->smallPictures()); + QCOMPARE(embeddedSizes(exportLikeTheDialog(diagram, false)), + QList{QSize(800, 600)}); + delete picture; + } +}; + +QET_APP_TEST_MAIN(tst_svgsmallpictures) +#include "tst_svgsmallpictures.moc"