mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-03 17:34:12 +02:00
Put a landscape folio on its standard sheet in --export-pdf
The PDF export sizes each page to its folio, and QPageSize rounds a size within 3 pt of a standard sheet to the sheet. QPageSize knows the sheets upright only (bar Ledger), so only portrait folios snapped: an A3 landscape folio came out as a 1190 x 841 pt page, while an A3 portrait one was 842 x 1191, the sheet. A wide folio is now matched upright and turned. tst_pdfpagesize exports a fixture with an A3 folio each way and checks both pages are A3; it fails without the change (1190 x 841). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+12
-5
@@ -171,11 +171,18 @@ int exportPdf(QETProject &project, const QString &output,
|
||||
for (Diagram *diagram : diagrams) {
|
||||
const QRect r = diagramRect(diagram);
|
||||
// Match the page to the diagram (in points: 1px @ 96dpi = 0.75pt).
|
||||
const QPageSize page(QSizeF(r.width() * 72.0 / 96.0,
|
||||
r.height() * 72.0 / 96.0),
|
||||
QPageSize::Point);
|
||||
writer.setPageSize(page);
|
||||
writer.setPageMargins(QMarginsF(0, 0, 0, 0));
|
||||
// QPageSize rounds a size within 3 pt of a standard sheet to the
|
||||
// sheet, but knows the sheets upright only (bar Ledger), so a wide
|
||||
// folio is matched upright and turned: otherwise an A3 landscape
|
||||
// folio became a 1190 x 841 pt page while an A3 portrait one was
|
||||
// 842 x 1191, the sheet.
|
||||
QSizeF points(r.width() * 72.0 / 96.0, r.height() * 72.0 / 96.0);
|
||||
const bool wide = points.width() > points.height();
|
||||
if (wide) points.transpose();
|
||||
writer.setPageLayout(QPageLayout(QPageSize(points, QPageSize::Point),
|
||||
wide ? QPageLayout::Landscape
|
||||
: QPageLayout::Portrait,
|
||||
QMarginsF(0, 0, 0, 0)));
|
||||
|
||||
if (first) {
|
||||
if (!painter.begin(&writer)) {
|
||||
|
||||
@@ -388,6 +388,18 @@ target_compile_definitions(tst_wiringlistexport PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# --export-pdf puts an A3 folio on an A3 page whichever way it is turned:
|
||||
# QPageSize matches standard sheets upright only, so a wide folio is matched
|
||||
# upright and turned. fixtures/pdf_page_a3.qet has one folio each way.
|
||||
add_executable(
|
||||
tst_pdfpagesize
|
||||
tst_pdfpagesize.cpp)
|
||||
add_test(NAME tst_pdfpagesize COMMAND tst_pdfpagesize)
|
||||
add_dependencies(tst_pdfpagesize qelectrotech)
|
||||
target_link_libraries(tst_pdfpagesize PRIVATE Qt::Test Qt::Gui)
|
||||
target_compile_definitions(tst_pdfpagesize PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# Hops where wires cross (issue #436): the geometry in wirehops.cpp, and
|
||||
# through the real binary, that --resave keeps the project setting and
|
||||
# adds nothing to a project without it.
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<project title="pdf page size" uuid="{63126da6-a8f2-5840-8149-a23a51406a32}" 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">pdf_page_a3</property>
|
||||
<property name="savedtime" show="1">22: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="27" colsize="58" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="1070" indexrev="" locmach="" order="1" plant="" rows="14" rowsize="75" title="" uuid="{270fad7d-7d9f-4198-a0e1-dceb22c883cc}" 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"/>
|
||||
</diagram>
|
||||
<diagram author="" auto_page_num="" cols="20" colsize="55" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="1535" indexrev="" locmach="" order="2" plant="" rows="15" rowsize="101" title="" uuid="{9cf88404-d198-4690-90f7-647fb7940f68}" 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"/>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="ca">Elements importats</name>
|
||||
<name lang="cs">Zavedené prvky</name>
|
||||
<name lang="da">Importerede elementer</name>
|
||||
<name lang="de">Importierte Elemente</name>
|
||||
<name lang="el">Εισηγμένα στοιχεία</name>
|
||||
<name lang="en">Imported elements</name>
|
||||
<name lang="es">Elementos importados</name>
|
||||
<name lang="fr">Éléments importés</name>
|
||||
<name lang="hr">Uvezeni elementi</name>
|
||||
<name lang="it">Elementi importati</name>
|
||||
<name lang="ja">インバートされた要素</name>
|
||||
<name lang="ko">전기 심볼 요소</name>
|
||||
<name lang="ko_KR">전기 심볼 요소</name>
|
||||
<name lang="nl">Elementen geïmporteerd</name>
|
||||
<name lang="nl_BE">Elementen geïmporteerd</name>
|
||||
<name lang="pl">Elementy importowane</name>
|
||||
<name lang="pt">Elementos importados</name>
|
||||
<name lang="pt_BR">Elementos importados</name>
|
||||
<name lang="ro">Elemente importate</name>
|
||||
<name lang="ru">Импортированные элементы</name>
|
||||
<name lang="sl">Uvoženi elementi</name>
|
||||
<name lang="sv">Importerade element</name>
|
||||
<name lang="tr">İthal öğeler</name>
|
||||
<name lang="uk">Імпортовані елементи</name>
|
||||
<name lang="zh">导入元件</name>
|
||||
</names>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -0,0 +1,84 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QtTest>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QPageSize>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QRegularExpression>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
// --export-pdf sizes each page to its folio, and QPageSize rounds a size
|
||||
// within 3 pt of a standard sheet to the sheet. It knows the sheets upright
|
||||
// only, so a wide folio used to miss: the fixture's A3 landscape folio
|
||||
// (frame 1190.25 x 840.75 pt) came out 1190 x 841 pt while its A3 portrait
|
||||
// folio came out 842 x 1191, the sheet. Both must land on A3.
|
||||
class tst_pdfpagesize : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
// The MediaBox of each page, in page order. Qt writes one uncompressed
|
||||
// "/MediaBox [0 0 w h]" per page object.
|
||||
QList<QSizeF> exportPages(const QString &project)
|
||||
{
|
||||
const QString home = m_dir.filePath(QStringLiteral("home"));
|
||||
const QString tmp = m_dir.filePath(QStringLiteral("tmp"));
|
||||
const QString out = m_dir.filePath(QStringLiteral("out.pdf"));
|
||||
QDir().mkpath(home);
|
||||
QDir().mkpath(tmp);
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/.config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/.local/share"));
|
||||
env.insert(QStringLiteral("TMPDIR"), tmp);
|
||||
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
|
||||
{QStringLiteral("--export-pdf"), project, out});
|
||||
if (!proc.waitForFinished(60000) || proc.exitCode() != 0)
|
||||
return {};
|
||||
|
||||
QFile file(out);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return {};
|
||||
const QString pdf = QString::fromLatin1(file.readAll());
|
||||
static const QRegularExpression box(
|
||||
QStringLiteral(R"(/MediaBox \[0 0 ([0-9.]+) ([0-9.]+)\])"));
|
||||
QList<QSizeF> pages;
|
||||
auto it = box.globalMatch(pdf);
|
||||
while (it.hasNext()) {
|
||||
const auto m = it.next();
|
||||
pages << QSizeF(m.captured(1).toDouble(), m.captured(2).toDouble());
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_dir.isValid());
|
||||
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
||||
}
|
||||
|
||||
void a3BothWays()
|
||||
{
|
||||
const QString fixture = QFINDTESTDATA("fixtures/pdf_page_a3.qet");
|
||||
QVERIFY2(!fixture.isEmpty(), "fixture project not found");
|
||||
|
||||
const QList<QSizeF> pages = exportPages(fixture);
|
||||
QCOMPARE(pages.size(), 2);
|
||||
|
||||
// Qt writes the sheet's whole points: 842 x 1191 for A3.
|
||||
const QSize a3 = QPageSize(QPageSize::A3).sizePoints();
|
||||
QCOMPARE(pages.at(0), QSizeF(a3.height(), a3.width())); // landscape
|
||||
QCOMPARE(pages.at(1), QSizeF(a3)); // portrait
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_pdfpagesize)
|
||||
#include "tst_pdfpagesize.moc"
|
||||
Reference in New Issue
Block a user