1
/***************************************************************************
2
* Copyright (c) 2023 WandererFan <wandererfan@gmail.com> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
26
#include <QApplication>
30
#include <QPaintEngine>
33
#include <QPrintDialog>
34
#include <QPrintPreviewDialog>
38
#include <App/Application.h>
39
#include <App/Document.h>
40
#include <App/DocumentObject.h>
41
#include <Base/Console.h>
42
#include <Base/Stream.h>
43
#include <Base/Tools.h>
44
#include <Gui/Application.h>
45
#include <Gui/Command.h>
46
#include <Gui/Document.h>
47
#include <Gui/ViewProvider.h>
49
#include <Mod/TechDraw/App/DrawPage.h>
50
#include <Mod/TechDraw/App/DrawPagePy.h>
51
#include <Mod/TechDraw/App/DrawTemplate.h>
52
#include <Mod/TechDraw/App/Preferences.h>
54
#include "PagePrinter.h"
57
#include "ViewProviderPage.h"
59
using namespace TechDrawGui;
60
using namespace TechDraw;
62
constexpr double A4Heightmm = 297.0;
63
constexpr double A4Widthmm = 210.0;
64
constexpr double mmPerInch = 25.4;
67
/* TRANSLATOR TechDrawGui::PagePrinter */
69
//TYPESYSTEM_SOURCE_ABSTRACT(TechDrawGui::PagePrinter)
71
PagePrinter::PagePrinter(ViewProviderPage* pageVp)
72
: m_vpPage(pageVp), m_orientation(QPageLayout::Landscape),
73
m_paperSize(QPageSize::A4), m_pagewidth(0.0), m_pageheight(0.0)
77
void PagePrinter::setScene(QGSPage* scene)
82
void PagePrinter::setDocumentName(const std::string& name) { m_documentName = name; }
85
//! retrieve the attributes of a DrawPage and its Template
86
PaperAttributes PagePrinter::getPaperAttributes(TechDraw::DrawPage* dPage)
88
PaperAttributes result;
92
double width = A4Widthmm;
93
double height = A4Heightmm;
94
auto pageTemplate(dynamic_cast<TechDraw::DrawTemplate*>(dPage->Template.getValue()));
96
width = pageTemplate->Width.getValue();
97
height = pageTemplate->Height.getValue();
99
result.pagewidth = width;
100
result.pageheight = height;
102
//Qt's page size determination assumes Portrait orientation. To get the right paper size
103
//we need to ask in the proper form.
104
QPageSize::PageSizeId paperSizeID =
105
QPageSize::id(QSizeF(std::min(width, height), std::max(width, height)),
106
QPageSize::Millimeter, QPageSize::FuzzyOrientationMatch);
107
result.paperSize = paperSizeID;
109
result.orientation = (QPageLayout::Orientation)dPage->getOrientation();
110
if (result.paperSize == QPageSize::Ledger) {
111
// Ledger size paper orientation is reversed inside Qt
112
result.orientation =(QPageLayout::Orientation)(1 - result.orientation);
118
void PagePrinter::getPaperAttributes()
120
PaperAttributes attr = getPaperAttributes(m_vpPage->getDrawPage());
121
m_pagewidth = attr.pagewidth;
122
m_pageheight = attr.pageheight;
123
m_paperSize = attr.paperSize;
124
m_orientation = attr.orientation;
127
//! construct a page layout object that reflects the characteristics of a DrawPage
129
void PagePrinter::makePageLayout(TechDraw::DrawPage* dPage, QPageLayout& pageLayout, double& width,
132
PaperAttributes attr = getPaperAttributes(dPage);
133
width = attr.pagewidth;
134
height = attr.pageheight;
135
pageLayout.setPageSize(QPageSize(attr.paperSize));
136
pageLayout.setOrientation(attr.orientation);
139
/// print the Page associated with the parent MDIViewPage as a Pdf file
140
void PagePrinter::printPdf(std::string file)
142
// Base::Console().Message("PP::printPdf(%s)\n", file.c_str());
144
Base::Console().Warning("PagePrinter - no file specified\n");
148
// set up the pdfwriter
149
QString outputFile = QString::fromUtf8(file.data(), file.size());
150
QPdfWriter pdfWriter(outputFile);
151
QPageLayout pageLayout = pdfWriter.pageLayout();
152
QString documentName = QString::fromUtf8(m_vpPage->getDrawPage()->getNameInDocument());
153
pdfWriter.setTitle(documentName);
154
// default pdfWriter dpi is 1200.
156
// set up the page layout
157
auto dPage = m_vpPage->getDrawPage();
158
double width = A4Heightmm;//default to A4 Landscape 297 x 210
159
double height = A4Widthmm;
160
makePageLayout(dPage, pageLayout, width, height);
161
pdfWriter.setPageLayout(pageLayout);
163
// first page does not respect page layout unless painter is created after
164
// pdfWriter layout is established.
165
QPainter painter(&pdfWriter);
168
QRectF sourceRect(0.0, Rez::guiX(-height), Rez::guiX(width), Rez::guiX(height));
169
double dpmm = pdfWriter.resolution() / mmPerInch;
170
int twide = int(std::round(width * dpmm));
171
int thigh = int(std::round(height * dpmm));
172
QRect targetRect(0, 0, twide, thigh);
173
renderPage(m_vpPage, painter, sourceRect, targetRect);
177
/// print the Page associated with the parent MDIViewPage
178
void PagePrinter::print(QPrinter* printer)
180
// Base::Console().Message("PP::print(printer)\n");
181
QPageLayout pageLayout = printer->pageLayout();
183
TechDraw::DrawPage* dp = m_vpPage->getDrawPage();
184
double width = A4Heightmm;//default to A4 Landscape 297 x 210
185
double height = A4Widthmm;
186
makePageLayout(dp, pageLayout, width, height);
187
printer->setPageLayout(pageLayout);
189
QPainter painter(printer);
191
QRect targetRect = printer->pageLayout().fullRectPixels(printer->resolution());
192
QRectF sourceRect(0.0, Rez::guiX(-height), Rez::guiX(width), Rez::guiX(height));
193
renderPage(m_vpPage, painter, sourceRect, targetRect);
196
//static routine to print all pages in a document
197
void PagePrinter::printAll(QPrinter* printer, App::Document* doc)
199
// Base::Console().Message("PP::printAll()\n");
201
QPageLayout pageLayout = printer->pageLayout();
202
std::vector<App::DocumentObject*> docObjs =
203
doc->getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
204
auto firstPage = docObjs.front();
206
auto dPage = static_cast<TechDraw::DrawPage*>(firstPage);
207
double width = A4Heightmm;//default to A4 Landscape 297 x 210
208
double height = A4Widthmm;
209
makePageLayout(dPage, pageLayout, width, height);
210
printer->setPageLayout(pageLayout);
211
QPainter painter(printer);
213
bool firstTime = true;
214
for (auto& obj : docObjs) {
215
Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(obj);
217
continue;// can't print this one
219
auto* vpp = dynamic_cast<TechDrawGui::ViewProviderPage*>(vp);
221
continue;// can't print this one
224
auto dPage = static_cast<TechDraw::DrawPage*>(obj);
225
double width = A4Heightmm;//default to A4 Landscape 297 x 210
226
double height = A4Widthmm;
227
makePageLayout(dPage, pageLayout, width, height);
228
printer->setPageLayout(pageLayout);
230
//for some reason the first page doesn't obey the pageLayout, so we have to print
231
//a sacrificial blank page, but we make it a feature instead of a bug by printing a
232
//table of contents on the sacrificial page.
233
// Note: if the painter(printer) occurs after the printer->setPageLayout, then the
234
// first page will obey the layout. This would mean creating the painter inside the
237
// firstTime = false;
238
// printBannerPage(printer, painter, pageLayout, doc, docObjs);
244
QRectF sourceRect(0.0, Rez::guiX(-height), Rez::guiX(width), Rez::guiX(height));
245
QRect targetRect = printer->pageLayout().fullRectPixels(printer->resolution());
247
renderPage(vpp, painter, sourceRect, targetRect);
252
//static routine to print all pages in a document to pdf
253
void PagePrinter::printAllPdf(QPrinter* printer, App::Document* doc)
255
// Base::Console().Message("PP::printAllPdf()\n");
256
double dpmm = printer->resolution() / mmPerInch;
258
QString outputFile = printer->outputFileName();
259
QString documentName = QString::fromUtf8(doc->getName());
260
QPdfWriter pdfWriter(outputFile);
261
// setPdfVersion sets the printed PDF Version to comply with PDF/A-1b, more details under: https://www.kdab.com/creating-pdfa-documents-qt/
262
// but this is not working as of Qt 5.12
263
//printer->setPdfVersion(QPagedPaintDevice::PdfVersion_A1b);
264
//pdfWriter.setPdfVersion(QPagedPaintDevice::PdfVersion_A1b);
265
pdfWriter.setTitle(documentName);
266
pdfWriter.setResolution(printer->resolution());
267
QPageLayout pageLayout = printer->pageLayout();
268
// we want to set the layout for the first page before we make the painter(&pdfWriter) or the layout for the first page will
270
std::vector<App::DocumentObject*> docObjs =
271
doc->getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
272
auto firstPage = docObjs.front();
274
auto dPage = static_cast<TechDraw::DrawPage*>(firstPage);
275
double width = A4Heightmm;//default to A4 Landscape 297 x 210
276
double height = A4Widthmm;
277
makePageLayout(dPage, pageLayout, width, height);
279
pdfWriter.setPageLayout(pageLayout);
280
// to get several pages into the same pdf, we must use the same painter for each page and not have any
281
// start() or end() until all the pages are printed.
282
QPainter painter(&pdfWriter);
284
bool firstTime = true;
285
for (auto& obj : docObjs) {
286
Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(obj);
288
continue;// can't print this one
290
auto vpp = dynamic_cast<TechDrawGui::ViewProviderPage*>(vp);
292
continue;// can't print this one
294
auto dPage = static_cast<TechDraw::DrawPage*>(obj);
297
makePageLayout(dPage, pageLayout, width, height);
298
pdfWriter.setPageLayout(pageLayout);
304
QRectF sourceRect(0.0, Rez::guiX(-height), Rez::guiX(width), Rez::guiX(height));
305
QRect targetRect(0, 0, width * dpmm, height * dpmm);
306
renderPage(vpp, painter, sourceRect, targetRect);
311
//! we don't need the banner page any more
312
void PagePrinter::printBannerPage(QPrinter* printer, QPainter& painter, QPageLayout& pageLayout,
313
App::Document* doc, std::vector<App::DocumentObject*>& docObjs)
315
QFont savePainterFont = painter.font();
317
painterFont.setFamily(Preferences::labelFontQString());
318
int fontSizeMM = Preferences::labelFontSizeMM();
319
double dpmm = printer->resolution() / mmPerInch;
320
int fontSizePx = fontSizeMM * dpmm;
321
painterFont.setPixelSize(fontSizePx);
322
painter.setFont(painterFont);
325
QString docLine = QObject::tr("Document Name:") + QLatin1String(" ") + QString::fromUtf8(doc->getName());
326
int leftMargin = pageLayout.margins().left() * dpmm + 5 * dpmm; //layout margin + 5mm
327
int verticalPos = pageLayout.margins().top() * dpmm + 20 * dpmm;//layout margin + 20mm
328
int verticalSpacing = 2; //double space
329
painter.drawText(leftMargin, verticalPos, docLine);
331
//leave some blank space between document name and page entries
332
verticalPos += 2 * verticalSpacing * fontSizePx;
333
for (auto& obj : docObjs) {
334
//print a line for each page
335
QString pageLine = QString::fromUtf8(obj->getNameInDocument()) + QString::fromUtf8(" / ")
336
+ QString::fromUtf8(obj->Label.getValue());
337
painter.drawText(leftMargin, verticalPos, pageLine);
338
verticalPos += verticalSpacing * fontSizePx;
340
painter.setFont(savePainterFont);//restore the original font
344
void PagePrinter::renderPage(ViewProviderPage* vpp, QPainter& painter, QRectF& sourceRect,
347
// Base::Console().Message("PP::renderPage()\n");
348
//turn off view frames for print
349
bool saveState = vpp->getFrameState();
350
vpp->setFrameState(false);
351
vpp->setTemplateMarkers(false);
353
//scene might be drawn in light text. we need to redraw in normal text.
354
bool saveLightOnDark = Preferences::lightOnDark();
355
if (Preferences::lightOnDark()) {
356
Preferences::lightOnDark(false);
357
vpp->getQGSPage()->redrawAllViews();
360
vpp->getQGSPage()->refreshViews();
361
vpp->getQGSPage()->render(&painter, targetRect, sourceRect);
364
vpp->setFrameState(saveState);
365
vpp->setTemplateMarkers(saveState);
366
Preferences::lightOnDark(saveLightOnDark);
368
vpp->getQGSPage()->refreshViews();
371
void PagePrinter::saveSVG(std::string file)
374
Base::Console().Warning("PagePrinter - no file specified\n");
377
QString filename = QString::fromUtf8(file.data(), file.size());
379
m_scene->saveSvg(filename);
383
void PagePrinter::saveDXF(std::string fileName)
385
TechDraw::DrawPage* page = m_vpPage->getDrawPage();
386
std::string PageName = page->getNameInDocument();
387
fileName = Base::Tools::escapeEncodeFilename(fileName);
388
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Save page to dxf"));
389
Gui::Command::doCommand(Gui::Command::Doc, "import TechDraw");
390
Gui::Command::doCommand(Gui::Command::Doc,
391
"TechDraw.writeDXFPage(App.activeDocument().%s, u\"%s\")",
392
PageName.c_str(), (const char*)fileName.c_str());
393
Gui::Command::commitCommand();
396
void PagePrinter::savePDF(std::string file)
398
// Base::Console().Message("PP::savePDF(%s)\n", file.c_str());
402
PaperAttributes::PaperAttributes()
404
// set default values to A4 Landscape
405
orientation = QPageLayout::Orientation::Landscape;
406
paperSize = QPageSize::A4;
407
pagewidth = A4Heightmm;
408
pageheight = A4Widthmm;