mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-14 09:14:14 +02:00
Add an SVG export option to store pictures smaller
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>
This commit is contained in:
@@ -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<Diagram *> 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
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace CLIExport {
|
||||
Usage:
|
||||
qelectrotech --export-pdf <project.qet> <output.pdf> [--show-terminals]
|
||||
qelectrotech --export-png <project.qet> <output_dir> [--show-terminals]
|
||||
qelectrotech --export-svg <project.qet> <output_dir> [--show-terminals]
|
||||
qelectrotech --export-svg <project.qet> <output_dir> [--show-terminals] [--svg-small-pictures]
|
||||
qelectrotech --export-dxf <project.qet> <output_dir> [--show-terminals] [--dxf-blocks] [--dxf-attributes]
|
||||
qelectrotech --export-cables <project.qet> <output.csv>
|
||||
qelectrotech --export-wires <project.qet> <output.csv>
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# 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(
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<project title="A large picture shown small" uuid="{3f6a2c91-8d47-4b5e-a0c3-91e2d7b4f600}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="saveddate" show="1">10/2/26</property>
|
||||
<property name="saveddate-eu" show="1">02-10-2026</property>
|
||||
<property name="saveddate-us" show="1">2026-10-02</property>
|
||||
<property name="savedfilename" show="1">svg_small_pictures</property>
|
||||
<property name="savedtime" show="1">18:39</property>
|
||||
</properties>
|
||||
<usage enabled="true" time_spent="0"/>
|
||||
<newdiagrams>
|
||||
<border cols="17" colsize="60" displaycols="true" displayrows="true" rows="8" rowsize="80"/>
|
||||
<inset author="" auto_page_num="" date="null" displayAt="bottom" filename="" folio="" indexrev="" locmach="" plant="" title="" version=""/>
|
||||
<conductors bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<report label="%f-%l%c"/>
|
||||
<xrefs>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="coil" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="commutator" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="plc" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="protection" xrefpos="AlignBottom"/>
|
||||
</xrefs>
|
||||
<conductors_autonums auto_break_conductors="false" current_autonum="" freeze_new_conductors="false"/>
|
||||
<folio_autonums/>
|
||||
<element_autonums current_autonum="" freeze_new_elements="false"/>
|
||||
<guides/>
|
||||
</newdiagrams>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="660" indexrev="" locmach="" order="1" plant="" rows="8" rowsize="80" title="" uuid="{3f6a2c91-8d47-4b5e-a0c3-91e2d7b4f610}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<inputs/>
|
||||
<images>
|
||||
<image z="0" rotation="0" y="100" x="100" size="0.1" is_movable="1">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</image>
|
||||
<image z="0" rotation="0" y="300" x="100" size="1" is_movable="1">iVBORw0KGgoAAAANSUhEUgAAACgAAAAeCAIAAADRv8uKAAAAP0lEQVR42u3PMQoAIAwEwRVC1F/7dFuxFoKwqS7XHNNgJSR0OMP1Pu+DQckFs2pYsWLFihUrVqxYsWLFir8Rb7mhAuQBqxaIAAAAAElFTkSuQmCC</image>
|
||||
</images>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="en">Imported elements</name>
|
||||
</names>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -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 <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"
|
||||
Reference in New Issue
Block a user