diff --git a/sources/cli_export.cpp b/sources/cli_export.cpp index d1e22412d..d99481345 100644 --- a/sources/cli_export.cpp +++ b/sources/cli_export.cpp @@ -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)) { diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index e9b296482..41951877e 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -395,6 +395,18 @@ target_compile_definitions(tst_wiringlistexport PRIVATE "QET_TEST_BINARY_PATH=\"$\"" "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=\"$\"") + # One text per potential: the number goes on the same conductor in every # run when two conductors tie for longest. Exports # fixtures/one_text_per_potential_tie.qet through the real binary, several diff --git a/tests/qttest/fixtures/pdf_page_a3.qet b/tests/qttest/fixtures/pdf_page_a3.qet new file mode 100644 index 000000000..b018581b2 --- /dev/null +++ b/tests/qttest/fixtures/pdf_page_a3.qet @@ -0,0 +1,63 @@ + + + 10/2/26 + 02-10-2026 + 2026-10-02 + pdf_page_a3 + 22:39 + + + + + + + + + + + + + + + + + + + + + + + + + + + + Elements importats + Zavedené prvky + Importerede elementer + Importierte Elemente + Εισηγμένα στοιχεία + Imported elements + Elementos importados + Éléments importés + Uvezeni elementi + Elementi importati + インバートされた要素 + 전기 심볼 요소 + 전기 심볼 요소 + Elementen geïmporteerd + Elementen geïmporteerd + Elementy importowane + Elementos importados + Elementos importados + Elemente importate + Импортированные элементы + Uvoženi elementi + Importerade element + İthal öğeler + Імпортовані елементи + 导入元件 + + + + diff --git a/tests/qttest/tst_pdfpagesize.cpp b/tests/qttest/tst_pdfpagesize.cpp new file mode 100644 index 000000000..ffc1c13fc --- /dev/null +++ b/tests/qttest/tst_pdfpagesize.cpp @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include + +#include +#include +#include +#include +#include +#include +#include + +// --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 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 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 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"