wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
CircuitExporter.cpp
Go to the documentation of this file.
1// Copyright 2015 - 2026, GIBIS-UNIFESP and the wiRedPanda contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
5
6#include <algorithm>
7
8#include <QImage>
9#include <QPageLayout>
10#include <QPageSize>
11#include <QPainter>
12#include <QPrinter>
13#include <QRectF>
14#include <QSizeF>
15
16#include "App/Core/Common.h"
17#include "App/Scene/Scene.h"
18
19namespace CircuitExporter {
20
21// Add a 64 px margin so elements at the scene boundary are not clipped.
22static QRectF paddedBoundingRect(Scene *scene)
23{
24 return scene->itemsBoundingRect().adjusted(-64, -64, 64, 64);
25}
26
27void renderToPdf(Scene *scene, const QString &filePath)
28{
29 QPrinter printer(QPrinter::HighResolution);
30 printer.setPageSize(QPageSize(QPageSize::A4));
31 // Landscape fits most circuits better than portrait.
32 printer.setPageOrientation(QPageLayout::Orientation::Landscape);
33 printer.setOutputFormat(QPrinter::PdfFormat);
34 printer.setOutputFileName(filePath);
35
36 QPainter painter;
37
38 if (!painter.begin(&printer)) {
39 throw PANDACEPTION_WITH_CONTEXT("CircuitExporter", "Could not print this circuit to PDF.");
40 }
41
42 scene->render(&painter, QRectF(), paddedBoundingRect(scene));
43 painter.end();
44}
45
46QImage renderScaledImage(Scene *scene, const QRectF &paddedRect)
47{
48 // Cap the output at a sane maximum dimension. paddedRect derives from element positions
49 // loaded from a .panda file (or set via the MCP create_element/move_element commands, which
50 // accept any finite coordinate); the only check on load (GraphicElementSerializer's position
51 // validation) rejects non-finite coordinates but never bounds magnitude, so a crafted or
52 // corrupted file — or a scripted MCP client — with one element at a large-but-finite
53 // position would otherwise size this image proportionally — potentially tens of gigabytes.
54 // Scale into the capped size instead of failing outright, the same "fit scene content to a
55 // fixed target" technique renderToPdf already uses via its fixed-size printer page. Below
56 // the cap (every realistic circuit), output resolution is unchanged.
57 QSizeF targetSize = paddedRect.size();
58 const qreal scale = (std::min)({1.0, kMaxImageDimension / targetSize.width(), kMaxImageDimension / targetSize.height()});
59 if (scale < 1.0) {
60 targetSize *= scale;
61 }
62
63 // QImage with an explicit alpha format, not QPixmap — QPixmap(size) defaults to an
64 // opaque platform format on this build, so filling it with Qt::transparent silently
65 // flattens to opaque instead of storing real alpha. Without an explicit fill at all it's
66 // uninitialized (Qt's documented QPixmap/QImage(size) contract): Scene only paints its
67 // background opaquely once Scene::updateTheme() has run, which production always does
68 // first, masking this everywhere else; a caller that doesn't would export genuine
69 // garbage/uninitialized pixels in the padding margin.
70 QImage image(targetSize.toSize(), QImage::Format_ARGB32_Premultiplied);
71 image.fill(Qt::transparent);
72
73 QPainter painter;
74 if (!painter.begin(&image)) {
75 throw PANDACEPTION_WITH_CONTEXT("CircuitExporter", "Could not begin painting circuit image.");
76 }
77 // Antialiasing enabled here; the PDF path relies on the printer's high DPI instead.
78 painter.setRenderHint(QPainter::Antialiasing);
79 scene->render(&painter, QRectF(QPointF(), targetSize), paddedRect);
80 painter.end();
81
82 return image;
83}
84
85void renderToImage(Scene *scene, const QString &filePath)
86{
87 const QImage image = renderScaledImage(scene, paddedBoundingRect(scene));
88
89 if (!image.save(filePath)) {
90 throw PANDACEPTION_WITH_CONTEXT("CircuitExporter", "Could not save image to %1.", filePath);
91 }
92}
93
94} // namespace CircuitExporter
CircuitExporter: render a Scene to PDF or PNG image.
Common logging utilities, the Pandaception error type, and helper macros.
#define PANDACEPTION_WITH_CONTEXT(context, msg,...)
Definition Common.h:102
Main circuit editing scene with undo/redo and user interaction.
Main circuit editing scene.
Definition Scene.h:56
Pure render functions for exporting a circuit scene to image formats.
static QRectF paddedBoundingRect(Scene *scene)
void renderToImage(Scene *scene, const QString &filePath)
Renders scene to a PNG image file at filePath.
QImage renderScaledImage(Scene *scene, const QRectF &paddedRect)
Renders scene into a transparent-filled QImage bounded by kMaxImageDimension.
constexpr double kMaxImageDimension
void renderToPdf(Scene *scene, const QString &filePath)
Renders scene to a PDF file at filePath.