Files
create/src/Mod/TechDraw/Gui/PagePrinter.cpp
Markus Reitböck 63ab3de853 TechDraw: use CMake to generate precompiled headers on all platforms
"Professional CMake" book suggest the following:

"Targets should build successfully with or without compiler support for precompiled headers. It
 should be considered an optimization, not a requirement. In particular, do not explicitly include a
 precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically
 generated precompile header on the compiler command line instead. This is more portable across
 the major compilers and is likely to be easier to maintain. It will also avoid warnings being
 generated from certain code checking tools like iwyu (include what you use)."

Therefore, removed the "#include <PreCompiled.h>" from sources, also
there is no need for the "#ifdef _PreComp_" anymore
2025-09-23 00:50:59 +02:00

448 lines
17 KiB
C++

/***************************************************************************
* Copyright (c) 2023 WandererFan <wandererfan@gmail.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include <QApplication>
#include <QMessageBox>
#include <QPageLayout>
#include <QPageSize>
#include <QPaintEngine>
#include <QPainter>
#include <QPdfWriter>
#include <QPrintDialog>
#include <QPrintPreviewDialog>
#include <QPrinter>
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <Base/Console.h>
#include <Base/Stream.h>
#include <Base/Tools.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/Document.h>
#include <Gui/ViewProvider.h>
#include <Gui/PreferencePages/DlgSettingsPDF.h>
#include <Mod/TechDraw/App/DrawPage.h>
#include <Mod/TechDraw/App/DrawPagePy.h>
#include <Mod/TechDraw/App/DrawTemplate.h>
#include <Mod/TechDraw/App/DrawUtil.h>
#include <Mod/TechDraw/App/Preferences.h>
#include "PagePrinter.h"
#include "QGSPage.h"
#include "Rez.h"
#include "ViewProviderPage.h"
using namespace TechDrawGui;
using namespace TechDraw;
using DU = DrawUtil;
constexpr double A4Heightmm = 297.0;
constexpr double A4Widthmm = 210.0;
constexpr double mmPerInch = 25.4;
/* TRANSLATOR TechDrawGui::PagePrinter */
//TYPESYSTEM_SOURCE_ABSTRACT(TechDrawGui::PagePrinter)
//! retrieve the attributes of a DrawPage and its Template
PaperAttributes PagePrinter::getPaperAttributes(TechDraw::DrawPage* dPage)
{
PaperAttributes result;
if (!dPage) {
return result;
}
double width = A4Widthmm;
double height = A4Heightmm;
auto pageTemplate(dynamic_cast<TechDraw::DrawTemplate*>(dPage->Template.getValue()));
if (pageTemplate) {
width = pageTemplate->Width.getValue();
height = pageTemplate->Height.getValue();
}
// result.m_pagewidth = width;
// result.m_pageheight = height;
//Qt's page size determination assumes Portrait orientation. To get the right paper size
//we need to ask in the proper form.
QPageSize::PageSizeId paperSizeID =
QPageSize::id(QSizeF(std::min(width, height), std::max(width, height)),
QPageSize::Millimeter, QPageSize::FuzzyOrientationMatch);
auto paperSize = paperSizeID;
auto orientation = (QPageLayout::Orientation)dPage->getOrientation();
if (paperSize == QPageSize::Ledger) {
// Ledger size paper orientation is reversed inside Qt
orientation = (QPageLayout::Orientation)(1 - orientation);
}
return {orientation, paperSize, width, height};
}
//! retrieve the attributes of a DrawPage by its viewProvider
PaperAttributes PagePrinter::getPaperAttributes(ViewProviderPage* vpPage)
{
auto page = vpPage->getDrawPage();
return getPaperAttributes(page);
}
//! construct a page layout object that reflects the characteristics of a DrawPage
void PagePrinter::makePageLayout(TechDraw::DrawPage* dPage, QPageLayout& pageLayout, double& width,
double& height)
{
PaperAttributes attr = getPaperAttributes(dPage);
width = attr.pageWidth();
height = attr.pageHeight();
pageLayout.setPageSize(QPageSize(attr.pageSize()));
pageLayout.setOrientation(attr.orientation());
pageLayout.setMode(QPageLayout::FullPageMode);
pageLayout.setMargins(QMarginsF());
}
//! print all pages in a document
void PagePrinter::printAll(QPrinter* printer, App::Document* doc)
{
QPageLayout pageLayout = printer->pageLayout();
std::vector<App::DocumentObject*> docObjs =
doc->getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
auto firstPage = docObjs.front();
auto dPage = static_cast<TechDraw::DrawPage*>(firstPage);
double width = A4Heightmm; // default to A4 Landscape 297 x 210
double height = A4Widthmm;
makePageLayout(dPage, pageLayout, width, height);
printer->setPageLayout(pageLayout);
QPainter painter(printer);
auto ourDoc = Gui::Application::Instance->getDocument(doc);
auto docModifiedState = ourDoc->isModified();
bool firstTime = true;
for (auto& obj : docObjs) {
Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(obj);
if (!vp) {
continue; // can't print this one
}
auto* vpp = dynamic_cast<TechDrawGui::ViewProviderPage*>(vp);
if (!vpp) {
continue; // can't print this one
}
auto dPage = static_cast<TechDraw::DrawPage*>(obj);
double width = A4Heightmm; // default to A4 Landscape 297 x 210
double height = A4Widthmm;
makePageLayout(dPage, pageLayout, width, height);
printer->setPageLayout(pageLayout);
if (!firstTime) {
printer->newPage();
}
firstTime = false;
QRectF sourceRect(0.0, Rez::guiX(-height), Rez::guiX(width), Rez::guiX(height));
QRect targetRect = printer->pageLayout().fullRectPixels(printer->resolution());
renderPage(vpp, painter, sourceRect, targetRect);
dPage->redrawCommand();
}
ourDoc->setModified(docModifiedState);
}
//! print all pages in a document to pdf
void PagePrinter::printAllPdf(QPrinter* printer, App::Document* doc)
{
double dpmm = printer->resolution() / mmPerInch;
QString outputFile = printer->outputFileName();
QString documentName = QString::fromUtf8(doc->getName());
QPdfWriter pdfWriter(outputFile);
// setPdfVersion sets the printed PDF Version to what is chosen in Preferences/Import-Export/PDF
// more details under: https://www.kdab.com/creating-pdfa-documents-qt/
pdfWriter.setPdfVersion(Gui::Dialog::DlgSettingsPDF::evaluatePDFVersion());
pdfWriter.setTitle(documentName);
pdfWriter.setCreator(QString::fromStdString(App::Application::getNameWithVersion())
+ QLatin1String(" TechDraw"));
pdfWriter.setResolution(printer->resolution());
QPageLayout pageLayout = printer->pageLayout();
// we want to set the layout for the first page before we make the painter(&pdfWriter) or the layout for the first page will
// not be correct.
std::vector<App::DocumentObject*> docObjs =
doc->getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
auto firstPage = docObjs.front();
auto dPage = static_cast<TechDraw::DrawPage*>(firstPage);
double width = A4Heightmm;//default to A4 Landscape 297 x 210
double height = A4Widthmm;
makePageLayout(dPage, pageLayout, width, height);
pdfWriter.setPageLayout(pageLayout);
// to get several pages into the same pdf, we must use the same painter for each page and not have any
// start() or end() until all the pages are printed.
QPainter painter(&pdfWriter);
auto ourDoc = Gui::Application::Instance->getDocument(doc);
auto docModifiedState = ourDoc->isModified();
bool firstTime = true;
for (auto& obj : docObjs) {
Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(obj);
if (!vp) {
continue;// can't print this one
}
auto vpp = dynamic_cast<TechDrawGui::ViewProviderPage*>(vp);
if (!vpp) {
continue;// can't print this one
}
auto ourScene = vpp->getQGSPage();
ourScene->setExportingPdf(true);
auto dPage = static_cast<TechDraw::DrawPage*>(obj);
double width{0};
double height{0};
makePageLayout(dPage, pageLayout, width, height);
pdfWriter.setPageLayout(pageLayout);
if (!firstTime) {
pdfWriter.newPage();
}
firstTime = false;
QRectF sourceRect(0.0, Rez::guiX(-height), Rez::guiX(width), Rez::guiX(height));
QRect targetRect(0, 0, width * dpmm, height * dpmm);
renderPage(vpp, painter, sourceRect, targetRect);
dPage->redrawCommand();
ourScene->setExportingPdf(true);
}
ourDoc->setModified(docModifiedState);
}
//! we don't need the banner page any more, but it might become useful again in the future.
void PagePrinter::printBannerPage(QPrinter* printer, QPainter& painter, QPageLayout& pageLayout,
App::Document* doc, std::vector<App::DocumentObject*>& docObjs)
{
QFont savePainterFont = painter.font();
QFont painterFont;
painterFont.setFamily(Preferences::labelFontQString());
int fontSizeMM = Preferences::labelFontSizeMM();
double dpmm = printer->resolution() / mmPerInch;
int fontSizePx = fontSizeMM * dpmm;
painterFont.setPixelSize(fontSizePx);
painter.setFont(painterFont);
//print a header
QString docLine = QObject::tr("Document Name:") + QLatin1String(" ") + QString::fromUtf8(doc->getName());
int leftMargin = pageLayout.margins().left() * dpmm + 5 * dpmm; //layout margin + 5mm
int verticalPos = pageLayout.margins().top() * dpmm + 20 * dpmm;//layout margin + 20mm
int verticalSpacing = 2; //double space
painter.drawText(leftMargin, verticalPos, docLine);
//leave some blank space between document name and page entries
verticalPos += 2 * verticalSpacing * fontSizePx;
for (auto& obj : docObjs) {
//print a line for each page
QString pageLine = QString::fromUtf8(obj->getNameInDocument()) + QStringLiteral(" / ")
+ QString::fromUtf8(obj->Label.getValue());
painter.drawText(leftMargin, verticalPos, pageLine);
verticalPos += verticalSpacing * fontSizePx;
}
painter.setFont(savePainterFont);//restore the original font
}
void PagePrinter::renderPage(ViewProviderPage* vpp, QPainter& painter, QRectF& sourceRect,
QRect& targetRect)
{
// Clear selection to avoid it being rendered to the file
vpp->getQGSPage()->clearSelection();
vpp->setTemplateMarkers(false);
//scene might be drawn in light text. we need to redraw in normal text.
bool saveLightOnDark = Preferences::lightOnDark();
if (Preferences::lightOnDark()) {
Preferences::lightOnDark(false);
vpp->getQGSPage()->redrawAllViews();
}
vpp->getQGSPage()->refreshViews();
vpp->getQGSPage()->render(&painter, targetRect, sourceRect);
// Reset
Preferences::lightOnDark(saveLightOnDark);
vpp->getQGSPage()->refreshViews();
}
/// print the Page associated with the view provider
void PagePrinter::print(ViewProviderPage* vpPage, QPrinter* printer)
{
QPageLayout pageLayout = printer->pageLayout();
TechDraw::DrawPage* dPage = vpPage->getDrawPage();
double width = A4Heightmm; // default to A4 Landscape 297 x 210
double height = A4Widthmm;
makePageLayout(dPage, pageLayout, width, height);
printer->setPageLayout(pageLayout);
QPainter painter(printer);
auto ourScene = vpPage->getQGSPage();
if (!printer->outputFileName().isEmpty()) {
ourScene->setExportingPdf(true);
}
auto ourDoc = Gui::Application::Instance->getDocument(dPage->getDocument());
auto docModifiedState = ourDoc->isModified();
QRect targetRect = printer->pageLayout().fullRectPixels(printer->resolution());
QRectF sourceRect(0.0, Rez::guiX(-height), Rez::guiX(width), Rez::guiX(height));
renderPage(vpPage, painter, sourceRect, targetRect);
ourScene->setExportingPdf(false); // doesn't hurt if not pdf
ourDoc->setModified(docModifiedState);
dPage->redrawCommand();
}
/// print the Page associated with the ViewProvider as a Pdf file
void PagePrinter::printPdf(ViewProviderPage* vpPage, const std::string& file)
{
if (file.empty()) {
Base::Console().warning("PagePrinter - no file specified\n");
return;
}
auto filespec = Base::Tools::escapeEncodeFilename(file);
filespec = DU::cleanFilespecBackslash(filespec);
// set up the pdfwriter
QString outputFile = QString::fromStdString(filespec);
QPdfWriter pdfWriter(outputFile);
pdfWriter.setPdfVersion(Gui::Dialog::DlgSettingsPDF::evaluatePDFVersion());
QPageLayout pageLayout = pdfWriter.pageLayout();
auto marginsdb = pageLayout.margins(QPageLayout::Millimeter);
QString documentName = QString::fromUtf8(vpPage->getDrawPage()->getNameInDocument());
pdfWriter.setTitle(documentName);
// default pdfWriter dpi is 1200.
pdfWriter.setCreator(QString::fromStdString(App::Application::getNameWithVersion())
+ QLatin1String(" TechDraw"));
// set up the page layout
auto dPage = vpPage->getDrawPage();
double width = A4Heightmm; // default to A4 Landscape 297 x 210
double height = A4Widthmm;
makePageLayout(dPage, pageLayout, width, height);
pdfWriter.setPageLayout(pageLayout);
marginsdb = pageLayout.margins(QPageLayout::Millimeter);
// first page does not respect page layout unless painter is created after
// pdfWriter layout is established.
QPainter painter(&pdfWriter);
auto ourScene = vpPage->getQGSPage();
ourScene->setExportingPdf(true);
auto ourDoc = Gui::Application::Instance->getDocument(dPage->getDocument());
auto docModifiedState = ourDoc->isModified();
// render the page
QRectF sourceRect(0.0, Rez::guiX(-height), Rez::guiX(width), Rez::guiX(height));
double dpmm = pdfWriter.resolution() / mmPerInch;
int twide = int(std::round(width * dpmm));
int thigh = int(std::round(height * dpmm));
QRect targetRect(0, 0, twide, thigh);
renderPage(vpPage, painter, sourceRect, targetRect);
ourScene->setExportingPdf(false);
ourDoc->setModified(docModifiedState);
dPage->redrawCommand();
}
//! save the page associated with the view provider as an svg file
void PagePrinter::saveSVG(ViewProviderPage* vpPage, const std::string& file)
{
if (file.empty()) {
Base::Console().warning("PagePrinter - no file specified\n");
return;
}
auto filespec = Base::Tools::escapeEncodeFilename(file);
filespec = DU::cleanFilespecBackslash(file);
QString filename = QString::fromStdString(filespec);
auto ourScene = vpPage->getQGSPage();
ourScene->setExportingSvg(true);
auto ourDoc = vpPage->getDocument();
auto docModifiedState = ourDoc->isModified();
ourScene->saveSvg(filename);
ourScene->setExportingSvg(false);
ourDoc->setModified(docModifiedState);
}
//! save the page associated with the view provider as an svg file
// Note: the dxf exporter does not modify the page, so we do not need to reset the modified flag
void PagePrinter::saveDXF(ViewProviderPage* vpPage, const std::string& inFileName)
{
TechDraw::DrawPage* page = vpPage->getDrawPage();
std::string PageName = page->getNameInDocument();
auto filespec = Base::Tools::escapeEncodeFilename(inFileName);
filespec = DU::cleanFilespecBackslash(filespec);
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Save page to DXF"));
Gui::Command::doCommand(Gui::Command::Doc, "import TechDraw");
Gui::Command::doCommand(Gui::Command::Doc,
"TechDraw.writeDXFPage(App.activeDocument().%s, u\"%s\")",
PageName.c_str(),
filespec.c_str());
Gui::Command::commitCommand();
}
// this one is somewhat superfluous (just a redirect).
void PagePrinter::savePDF(ViewProviderPage* vpPage, const std::string& file)
{
printPdf(vpPage, file);
}
PaperAttributes::PaperAttributes()
{
// set default values to A4 Landscape
m_orientation = QPageLayout::Orientation::Landscape;
m_paperSize = QPageSize::A4;
m_pagewidth = A4Heightmm;
m_pageheight = A4Widthmm;
}