Merge branch 'master' into qt6_cmake_joshua

This commit is contained in:
joshua
2026-07-31 21:15:21 +02:00
parent f16cf7dac8
commit e6d29cf345
297 changed files with 82691 additions and 27463 deletions
+137 -21
View File
@@ -18,25 +18,30 @@
#include "projectprintwindow.h"
#include "../diagram.h"
#include "../pdf_links.h"
#include "../qeticons.h"
#include "../qetproject.h"
#include "../qetversion.h"
#include "../qetgraphicsitem/crossrefitem.h"
#include "../qetgraphicsitem/dynamicelementtextitem.h"
#include "../qetgraphicsitem/elementtextitemgroup.h"
#include "ui_projectprintwindow.h"
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
# include <QDesktopWidget>
#else
# if TODO_LIST
# pragma message("@TODO remove code for QT 6 or later")
# endif
#endif
// Private Qt PDF engine for drawHyperlink() — not public API, stable since Qt4
// Requires QT += gui-private in qelectrotech.pro
#include <private/qpdf_p.h>
#include <QMarginsF>
#include <QPageSetupDialog>
#include <QPainter>
#include <QPrintDialog>
#include <QPrintPreviewWidget>
#include <QScreen>
#include <QFile>
#include <QRegularExpression>
#include <QMap>
#include <QTimer>
#include <QVector>
/**
* @brief ProjectPrintWindow::ProjectPrintWindow
@@ -66,14 +71,10 @@ void ProjectPrintWindow::launchDialog(QETProject *project, QPrinter::OutputForma
print_dialog.setWindowFlags(Qt::Sheet);
#endif
print_dialog.setWindowTitle(tr("Options d'impression", "window title"));
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
print_dialog.setEnabledOptions(QAbstractPrintDialog::PrintShowPageSize);
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
qDebug()<<"Help code for QT 6 or later";
#endif
// setOptions() is the modern spelling of the Qt4-era
// setEnabledOptions() (removed in Qt 6): replace the enabled
// option set with just PrintShowPageSize, on Qt 5 and 6 alike.
print_dialog.setOptions(QAbstractPrintDialog::PrintShowPageSize);
if (print_dialog.exec() == QDialog::Rejected) {
delete printer_;
return;
@@ -190,7 +191,6 @@ ProjectPrintWindow::~ProjectPrintWindow()
*/
void ProjectPrintWindow::requestPaint()
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
#ifdef Q_OS_WIN
auto screen = this->screen();
if(screen)
@@ -210,20 +210,56 @@ void ProjectPrintWindow::requestPaint()
qDebug() << "--";
#endif
}
#endif
#endif
if (!m_project->diagrams().count()) {
return;
}
// Build diagram -> first physical PDF page number map (1-based)
// Must be done before the print loop since page numbers depend on order
QMap<Diagram*, int> diagramPageMap;
{
int pageNum = 1;
for (auto diagram : selectedDiagram()) {
diagramPageMap.insert(diagram, pageNum);
// Each diagram may span multiple pages if not fit_page
if (!ui->m_fit_in_page_cb->isChecked()) {
auto option = exportProperties();
bool full_page = m_printer->fullPage();
int h = horizontalPagesCount(diagram, option, full_page);
int v = verticalPagesCount(diagram, option, full_page);
pageNum += h * v;
} else {
pageNum += 1;
}
}
}
bool first = true;
QPainter painter(m_printer);
// A real PDF export uses the QPdfEngine; the on-screen preview uses a
// preview paint engine. We only post-process when actually writing a PDF.
const bool pdfExport =
(m_printer->outputFormat() == QPrinter::PdfFormat)
&& (dynamic_cast<QPdfEngine*>(painter.paintEngine()) != nullptr);
// plc-user: added because of "Warning: unused variable 'pdfExport'":
// should be fixed by original author by evaluating function-result!
(void)pdfExport;
for (auto diagram : selectedDiagram())
{
first ? first = false : m_printer->newPage();
printDiagram(diagram, ui->m_fit_in_page_cb->isChecked(), &painter, m_printer);
printDiagram(diagram, ui->m_fit_in_page_cb->isChecked(), &painter, m_printer, diagramPageMap);
}
// Note: do NOT call painter.end() or pdfConvertUriToGoTo() here.
// We are inside the paintRequested slot: the QPrintPreviewWidget still
// owns the paint cycle. On macOS arm64 (Metal/CALayer compositor),
// closing the QPainter manually inside this slot leaves the backing
// store in an undefined state, producing a black screen after export.
// pdfConvertUriToGoTo() is deferred to print() via QTimer::singleShot(0).
}
/**
@@ -233,7 +269,7 @@ void ProjectPrintWindow::requestPaint()
* @param fit_page
* @param printer
*/
void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer)
void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer, const QMap<Diagram*, int> &diagramPageMap)
{
////Prepare the print////
@@ -320,6 +356,65 @@ void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter
}
}
////Inject PDF cross-reference links////
if (printer->outputFormat() == QPrinter::PdfFormat && fit_page) {
auto *pdfEngine = dynamic_cast<QPdfEngine*>(painter->paintEngine());
if (pdfEngine) {
// QGraphicsScene::render() fait save()/restore() : worldTransform()
// est revenu a l'identite ici. On reconstruit DONC explicitement la
// transform appliquee par :
// diagram->render(painter, QRectF(), diagram_rect, KeepAspectRatio)
// cible vide => painter->viewport() ; source = diagram_rect ; centre.
const QRectF target = QRectF(painter->viewport());
const QRectF source = QRectF(diagram_rect); // meme source que render()
// render() ANCRE en haut-gauche (pas de centrage) :
// translate(target.topLeft) . scale(s,s) . translate(-source.topLeft)
// On reproduit EXACTEMENT ca — surtout PAS de (target-source*s)/2.
const qreal s = qMin(target.width() / source.width(),
target.height() / source.height());
QTransform fit;
fit.translate(target.x(), target.y());
fit.scale(s, s);
fit.translate(-source.x(), -source.y()); // scene -> pixels device
// IMPORTANT : QPdfEngine::drawHyperlink() applique lui-meme
// pageMatrix() (echelle 72/resolution + inversion de Y + marges).
// On lui passe donc le rectangle en PIXELS DEVICE, sans aucune
// conversion en points ni flip de notre cote.
const QRectF pageBounds(0, 0, target.width(), target.height());
// ---- Device-pixels -> PDF points, replicating QPdfEnginePrivate::pageMatrix()
// (same geometry for every page: same printer, page size and margins). ----
const qreal pt_scale = 72.0 / printer->resolution();
const qreal fullH_pt = printer->pageLayout().fullRectPoints().height();
const bool fullPageMode =
(printer->pageLayout().mode() == QPageLayout::FullPageMode);
const QRect paintPx =
printer->pageLayout().paintRectPixels(printer->resolution());
auto devToPdf = [=](const QPointF &d) -> QPointF {
qreal dx = d.x(), dy = d.y();
if (!fullPageMode) { dx += paintPx.left(); dy += paintPx.top(); }
return QPointF(pt_scale * dx, fullH_pt - pt_scale * dy);
};
PdfLinks::PageGeometry geom;
geom.sceneToDevice = fit;
geom.target = target;
geom.pageBounds = pageBounds;
geom.devToPdf = devToPdf;
geom.sourceRectOf = [this](Diagram *dg) {
return QRectF(diagramRect(dg, exportProperties()));
};
PdfLinks::injectCrossRefLinks(
pdfEngine, diagram, geom, diagramPageMap,
printer->outputFileName());
}
}
////PDF links end////
////Print is finished, restore diagram and graphics item properties
for (auto view : diagram->views()) {
view->setInteractive(true);
@@ -416,6 +511,7 @@ ExportProperties ProjectPrintWindow::exportProperties() const
exp.draw_terminals = ui->m_draw_terminal_cb->isChecked();
exp.draw_colored_conductors = ui->m_keep_conductor_color_cb->isChecked();
exp.draw_grid = false;
exp.draw_guides = false;
return exp;
}
@@ -775,9 +871,29 @@ void ProjectPrintWindow::on_m_uncheck_all_clicked()
void ProjectPrintWindow::print()
{
m_preview->print();
const bool isPdf = (m_printer->outputFormat() == QPrinter::PdfFormat);
const QString pdfFile = isPdf ? m_printer->outputFileName() : QString();
m_preview->print(); // triggers requestPaint() synchronously; painter
// is created/destroyed inside that call
savePageSetupForCurrentPrinter();
this->close();
if (isPdf && !pdfFile.isEmpty()) {
// Defer post-processing and window close to the next event-loop
// iteration. This lets the macOS arm64 Metal compositor finish
// compositing the backing store before the window is destroyed,
// which prevents the black screen observed on Apple Silicon under
// macOS Sequoia (QPrintPreviewWidget + CALayer timing issue).
QTimer::singleShot(0, this, [this, pdfFile]() {
// Convert URI link annotations into native internal GoTo/FitR
// actions so cross-references jump inside the document.
PdfLinks::convertUriToGoTo(pdfFile);
this->close();
});
} else {
this->close();
}
}
void ProjectPrintWindow::on_m_date_cb_userDateChanged(const QDate &date)
+2 -1
View File
@@ -21,6 +21,7 @@
#include "../exportproperties.h"
#include <QMainWindow>
#include <QMap>
#include <QPrinter>
namespace Ui {
@@ -79,7 +80,7 @@ class ProjectPrintWindow : public QMainWindow
private:
void requestPaint();
void printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer);
void printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer, const QMap<Diagram*, int> &diagramPageMap = {});
QRect diagramRect(Diagram *diagram, const ExportProperties &option) const;
int horizontalPagesCount(Diagram *diagram, const ExportProperties &option, bool full_page) const;
int verticalPagesCount(Diagram *diagram, const ExportProperties &option, bool full_page) const;