mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-14 09:14:14 +02:00
da6eb83035
An SVG keeps every picture at full resolution, whatever size it is drawn at: a folio with 22 phone photos shown at a tenth of their size gave a 270 MB SVG and took 65 s to write, most of it compressing the photos again. The export dialog gets "SVG: smaller pictures" (off by default, kept with the other export settings), and --export-svg gets --svg-small-pictures. With it, a picture is stored at four times the size it is drawn at when that is smaller than the picture; smaller pictures stay whole. Four times is about 375 dpi on a printed folio. The exporter sets Diagram::smallPictures() for its own drawing only, so screen, print, PDF and PNG are unchanged. It is not part of applyProperties(), which every format uses. On that project: --export-svg 84.9 s -> 34.5 s (opening is 20 s of both), 270 MB -> 57 MB, peak memory 2.45 -> 1.86 GB. Rendered at twice the folio size, the two SVGs differ by more than 32/255 on 0.23 % of pixels, along photo detail. tst_svgsmallpictures: an 800x600 picture drawn at 0.1 is stored at 320x240 with the option, through --export-svg and through the dialog's QPicture route, and at 800x600 without it; a 40x30 picture stays whole. It fails when the factor is changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
144 lines
4.8 KiB
C++
144 lines
4.8 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "qetapptest.h"
|
|
#include "qetgraphicsitem/diagramimageitem.h"
|
|
|
|
#include <QBuffer>
|
|
#include <QDomDocument>
|
|
#include <QPicture>
|
|
#include <QProcess>
|
|
#include <QRegularExpression>
|
|
#include <QSvgGenerator>
|
|
#include <QTemporaryDir>
|
|
|
|
/**
|
|
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<QSize> embeddedSizes(const QByteArray &svg)
|
|
{
|
|
static const QRegularExpression data(
|
|
QStringLiteral("data:image/png;base64,([A-Za-z0-9+/=\\s]+)"));
|
|
QList<QSize> 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>{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>{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>{QSize(320, 240)});
|
|
// The option lasts for one export only
|
|
QVERIFY(!diagram->smallPictures());
|
|
QCOMPARE(embeddedSizes(exportLikeTheDialog(diagram, false)),
|
|
QList<QSize>{QSize(800, 600)});
|
|
delete picture;
|
|
}
|
|
};
|
|
|
|
QET_APP_TEST_MAIN(tst_svgsmallpictures)
|
|
#include "tst_svgsmallpictures.moc"
|