2017-04-27 20:26:05 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2015-04-21 18:05:20 +02:00
|
|
|
#include "printer.h"
|
|
|
|
#include "templatelayout.h"
|
2021-08-04 07:27:41 +02:00
|
|
|
#include "core/dive.h" // for get_dive_by_uniq_id()
|
2016-04-05 09:40:03 -07:00
|
|
|
#include "core/statistics.h"
|
2018-06-03 22:15:19 +02:00
|
|
|
#include "core/qthelper.h"
|
2021-08-04 07:27:41 +02:00
|
|
|
#include "profile-widget/profilescene.h"
|
2015-04-21 18:05:20 +02:00
|
|
|
|
2015-08-11 22:50:27 +02:00
|
|
|
#include <algorithm>
|
2021-08-04 07:27:41 +02:00
|
|
|
#include <memory>
|
2015-04-21 18:05:20 +02:00
|
|
|
#include <QPainter>
|
2020-12-13 13:32:40 +01:00
|
|
|
#include <QPrinter>
|
2016-08-09 00:12:12 +02:00
|
|
|
#include <QtWebKitWidgets>
|
2015-06-10 19:31:19 +02:00
|
|
|
#include <QWebElementCollection>
|
|
|
|
#include <QWebElement>
|
2015-04-21 18:05:20 +02:00
|
|
|
|
2021-02-13 22:43:55 +01:00
|
|
|
Printer::Printer(QPaintDevice *paintDevice, const print_options &printOptions, const template_options &templateOptions, PrintMode printMode, bool inPlanner) :
|
2020-12-12 13:28:36 +01:00
|
|
|
paintDevice(paintDevice),
|
|
|
|
webView(new QWebView),
|
|
|
|
printOptions(printOptions),
|
|
|
|
templateOptions(templateOptions),
|
|
|
|
printMode(printMode),
|
2021-02-13 22:43:55 +01:00
|
|
|
inPlanner(inPlanner),
|
2021-07-07 10:21:40 +02:00
|
|
|
done(0)
|
2015-04-21 18:05:20 +02:00
|
|
|
{
|
2015-07-10 14:59:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Printer::~Printer()
|
|
|
|
{
|
|
|
|
delete webView;
|
2015-04-21 18:05:20 +02:00
|
|
|
}
|
|
|
|
|
2020-12-13 13:32:40 +01:00
|
|
|
void Printer::putProfileImage(const QRect &profilePlaceholder, const QRect &viewPort, QPainter *painter,
|
2021-08-04 07:27:41 +02:00
|
|
|
struct dive *dive, ProfileScene *profile)
|
2015-06-10 19:31:19 +02:00
|
|
|
{
|
|
|
|
int x = profilePlaceholder.x() - viewPort.x();
|
|
|
|
int y = profilePlaceholder.y() - viewPort.y();
|
|
|
|
// use the placeHolder and the viewPort position to calculate the relative position of the dive profile.
|
|
|
|
QRect pos(x, y, profilePlaceholder.width(), profilePlaceholder.height());
|
2021-05-06 14:45:09 +02:00
|
|
|
|
2021-08-30 22:59:45 +02:00
|
|
|
profile->draw(painter, pos, dive, 0, nullptr, false);
|
2015-06-10 19:31:19 +02:00
|
|
|
}
|
|
|
|
|
2015-08-13 23:23:07 +02:00
|
|
|
void Printer::flowRender()
|
|
|
|
{
|
2015-08-19 23:02:58 +02:00
|
|
|
// add extra padding at the bottom to pages with height not divisible by view port
|
|
|
|
int paddingBottom = pageSize.height() - (webView->page()->mainFrame()->contentsSize().height() % pageSize.height());
|
|
|
|
QString styleString = QString::fromUtf8("padding-bottom: ") + QString::number(paddingBottom) + "px;";
|
|
|
|
webView->page()->mainFrame()->findFirstElement("body").setAttribute("style", styleString);
|
|
|
|
|
2015-08-13 23:23:07 +02:00
|
|
|
// render the Qwebview
|
|
|
|
QPainter painter;
|
|
|
|
QRect viewPort(0, 0, 0, 0);
|
|
|
|
painter.begin(paintDevice);
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
|
|
|
|
|
|
|
// get all references to dontbreak divs
|
|
|
|
int start = 0, end = 0;
|
|
|
|
int fullPageResolution = webView->page()->mainFrame()->contentsSize().height();
|
2019-04-01 22:15:19 +02:00
|
|
|
const QWebElementCollection dontbreak = webView->page()->mainFrame()->findAllElements(".dontbreak");
|
|
|
|
for (QWebElement dontbreakElement: dontbreak) {
|
2015-08-13 23:23:07 +02:00
|
|
|
if ((dontbreakElement.geometry().y() + dontbreakElement.geometry().height()) - start < pageSize.height()) {
|
|
|
|
// One more element can be placed
|
|
|
|
end = dontbreakElement.geometry().y() + dontbreakElement.geometry().height();
|
|
|
|
} else {
|
2015-08-14 21:37:12 +02:00
|
|
|
// fill the page with background color
|
|
|
|
QRect fullPage(0, 0, pageSize.width(), pageSize.height());
|
2020-12-12 13:28:36 +01:00
|
|
|
QBrush fillBrush(templateOptions.color_palette.color1);
|
2015-08-14 21:37:12 +02:00
|
|
|
painter.fillRect(fullPage, fillBrush);
|
2015-08-13 23:23:07 +02:00
|
|
|
QRegion reigon(0, 0, pageSize.width(), end - start);
|
|
|
|
viewPort.setRect(0, start, pageSize.width(), end - start);
|
|
|
|
|
|
|
|
// render the base Html template
|
|
|
|
webView->page()->mainFrame()->render(&painter, QWebFrame::ContentsLayer, reigon);
|
|
|
|
|
|
|
|
// scroll the webview to the next page
|
|
|
|
webView->page()->mainFrame()->scroll(0, dontbreakElement.geometry().y() - start);
|
|
|
|
|
|
|
|
// rendering progress is 4/5 of total work
|
2017-03-23 08:13:49 +07:00
|
|
|
emit(progessUpdated(lrint((end * 80.0 / fullPageResolution) + done)));
|
2015-08-21 11:02:10 +02:00
|
|
|
|
|
|
|
// add new pages only in print mode, while previewing we don't add new pages
|
2019-05-15 07:42:14 -07:00
|
|
|
if (printMode == Printer::PRINT) {
|
2015-08-21 11:02:10 +02:00
|
|
|
static_cast<QPrinter*>(paintDevice)->newPage();
|
2019-05-15 07:42:14 -07:00
|
|
|
} else {
|
2015-08-21 11:02:10 +02:00
|
|
|
painter.end();
|
|
|
|
return;
|
|
|
|
}
|
2015-08-13 23:23:07 +02:00
|
|
|
start = dontbreakElement.geometry().y();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// render the remianing page
|
2015-08-14 21:37:12 +02:00
|
|
|
QRect fullPage(0, 0, pageSize.width(), pageSize.height());
|
2020-12-12 13:28:36 +01:00
|
|
|
QBrush fillBrush(templateOptions.color_palette.color1);
|
2015-08-14 21:37:12 +02:00
|
|
|
painter.fillRect(fullPage, fillBrush);
|
2015-08-13 23:23:07 +02:00
|
|
|
QRegion reigon(0, 0, pageSize.width(), end - start);
|
|
|
|
webView->page()->mainFrame()->render(&painter, QWebFrame::ContentsLayer, reigon);
|
|
|
|
|
|
|
|
painter.end();
|
|
|
|
}
|
|
|
|
|
2021-02-13 18:58:14 +01:00
|
|
|
void Printer::render(int pages)
|
2015-04-21 18:05:20 +02:00
|
|
|
{
|
2021-05-31 22:29:00 +02:00
|
|
|
// get all refereces to diveprofile class in the Html template
|
|
|
|
QWebElementCollection collection = webView->page()->mainFrame()->findAllElements(".diveprofile");
|
|
|
|
|
|
|
|
// A "standard" profile has about 600 pixels in height.
|
2021-08-09 16:48:08 +02:00
|
|
|
// Scale the items in the printed profile accordingly.
|
2021-05-31 22:29:00 +02:00
|
|
|
// This is arbitrary, but it seems to work reasonably well.
|
2021-08-09 16:48:08 +02:00
|
|
|
double dpr = collection.count() > 0 ? collection[0].geometry().size().height() / 600.0 : 1.0;
|
|
|
|
auto profile = std::make_unique<ProfileScene>(dpr, true, !printOptions.color_selected);
|
2015-06-10 19:31:19 +02:00
|
|
|
|
|
|
|
// render the Qwebview
|
2015-04-21 18:05:20 +02:00
|
|
|
QPainter painter;
|
2015-06-17 17:05:14 +02:00
|
|
|
QRect viewPort(0, 0, pageSize.width(), pageSize.height());
|
2015-07-10 20:34:25 +02:00
|
|
|
painter.begin(paintDevice);
|
2015-04-21 18:05:20 +02:00
|
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
2015-06-14 06:34:58 +02:00
|
|
|
|
2015-06-10 19:31:19 +02:00
|
|
|
int elemNo = 0;
|
2021-02-13 18:58:14 +01:00
|
|
|
for (int i = 0; i < pages; i++) {
|
2015-06-10 19:31:19 +02:00
|
|
|
// render the base Html template
|
2015-04-21 18:05:20 +02:00
|
|
|
webView->page()->mainFrame()->render(&painter, QWebFrame::ContentsLayer);
|
2015-06-10 19:31:19 +02:00
|
|
|
|
|
|
|
// render all the dive profiles in the current page
|
|
|
|
while (elemNo < collection.count() && collection.at(elemNo).geometry().y() < viewPort.y() + viewPort.height()) {
|
|
|
|
// dive id field should be dive_{{dive_no}} se we remove the first 5 characters
|
2015-07-28 13:35:04 -07:00
|
|
|
QString diveIdString = collection.at(elemNo).attribute("id");
|
|
|
|
int diveId = diveIdString.remove(0, 5).toInt(0, 10);
|
2021-04-02 18:48:33 +02:00
|
|
|
putProfileImage(collection.at(elemNo).geometry(), viewPort, &painter, get_dive_by_uniq_id(diveId), profile.get());
|
2015-06-10 19:31:19 +02:00
|
|
|
elemNo++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// scroll the webview to the next page
|
2015-06-17 17:05:14 +02:00
|
|
|
webView->page()->mainFrame()->scroll(0, pageSize.height());
|
|
|
|
viewPort.adjust(0, pageSize.height(), 0, pageSize.height());
|
2015-06-10 19:31:19 +02:00
|
|
|
|
|
|
|
// rendering progress is 4/5 of total work
|
2021-02-13 18:58:14 +01:00
|
|
|
emit(progessUpdated(lrint((i * 80.0 / pages) + done)));
|
|
|
|
if (i < pages - 1 && printMode == Printer::PRINT)
|
2015-07-10 20:34:25 +02:00
|
|
|
static_cast<QPrinter*>(paintDevice)->newPage();
|
2015-04-21 18:05:20 +02:00
|
|
|
}
|
|
|
|
painter.end();
|
|
|
|
}
|
|
|
|
|
2015-05-30 15:32:15 +02:00
|
|
|
//value: ranges from 0 : 100 and shows the progress of the templating engine
|
|
|
|
void Printer::templateProgessUpdated(int value)
|
|
|
|
{
|
|
|
|
done = value / 5; //template progess if 1/5 of total work
|
|
|
|
emit progessUpdated(done);
|
|
|
|
}
|
|
|
|
|
2020-12-12 13:28:36 +01:00
|
|
|
QString Printer::exportHtml()
|
|
|
|
{
|
2019-09-06 23:17:49 +02:00
|
|
|
TemplateLayout t(printOptions, templateOptions);
|
|
|
|
connect(&t, SIGNAL(progressUpdated(int)), this, SLOT(templateProgessUpdated(int)));
|
|
|
|
QString html;
|
|
|
|
|
2020-12-12 13:28:36 +01:00
|
|
|
if (printOptions.type == print_options::DIVELIST)
|
2021-02-13 22:43:55 +01:00
|
|
|
html = t.generate(inPlanner);
|
2020-12-12 13:28:36 +01:00
|
|
|
else if (printOptions.type == print_options::STATISTICS )
|
2019-09-06 23:17:49 +02:00
|
|
|
html = t.generateStatistics();
|
|
|
|
|
|
|
|
// TODO: write html to file
|
|
|
|
return html;
|
|
|
|
}
|
|
|
|
|
2015-04-21 18:05:20 +02:00
|
|
|
void Printer::print()
|
|
|
|
{
|
2015-07-10 21:30:18 +02:00
|
|
|
// we can only print if "PRINT" mode is selected
|
|
|
|
if (printMode != Printer::PRINT) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-10 20:34:25 +02:00
|
|
|
QPrinter *printerPtr;
|
|
|
|
printerPtr = static_cast<QPrinter*>(paintDevice);
|
|
|
|
|
2015-07-02 22:26:31 +02:00
|
|
|
TemplateLayout t(printOptions, templateOptions);
|
2015-06-17 17:05:14 +02:00
|
|
|
connect(&t, SIGNAL(progressUpdated(int)), this, SLOT(templateProgessUpdated(int)));
|
2021-07-07 10:21:40 +02:00
|
|
|
int dpi = printerPtr->resolution();
|
2015-06-17 17:05:14 +02:00
|
|
|
//rendering resolution = selected paper size in inchs * printer dpi
|
2015-08-22 16:05:13 +02:00
|
|
|
pageSize.setHeight(qCeil(printerPtr->pageRect(QPrinter::Inch).height() * dpi));
|
|
|
|
pageSize.setWidth(qCeil(printerPtr->pageRect(QPrinter::Inch).width() * dpi));
|
2015-06-17 17:05:14 +02:00
|
|
|
webView->page()->setViewportSize(pageSize);
|
2015-08-08 00:55:12 +02:00
|
|
|
webView->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
|
2019-03-19 12:40:33 -07:00
|
|
|
|
2015-08-11 22:50:27 +02:00
|
|
|
// export border width with at least 1 pixel
|
2020-12-12 13:28:36 +01:00
|
|
|
// templateOptions.borderwidth = std::max(1, pageSize.width() / 1000);
|
|
|
|
if (printOptions.type == print_options::DIVELIST)
|
2021-02-13 22:43:55 +01:00
|
|
|
webView->setHtml(t.generate(inPlanner));
|
2020-12-12 13:28:36 +01:00
|
|
|
else if (printOptions.type == print_options::STATISTICS )
|
2015-08-21 19:12:33 +02:00
|
|
|
webView->setHtml(t.generateStatistics());
|
2020-12-12 13:28:36 +01:00
|
|
|
if (printOptions.color_selected && printerPtr->colorMode())
|
2015-07-10 20:34:25 +02:00
|
|
|
printerPtr->setColorMode(QPrinter::Color);
|
2020-12-12 13:28:36 +01:00
|
|
|
else
|
2015-07-10 20:34:25 +02:00
|
|
|
printerPtr->setColorMode(QPrinter::GrayScale);
|
2015-07-10 16:20:14 +02:00
|
|
|
// apply user settings
|
|
|
|
int divesPerPage;
|
|
|
|
|
|
|
|
// get number of dives per page from data-numberofdives attribute in the body of the selected template
|
|
|
|
bool ok;
|
|
|
|
divesPerPage = webView->page()->mainFrame()->findFirstElement("body").attribute("data-numberofdives").toInt(&ok);
|
|
|
|
if (!ok) {
|
|
|
|
divesPerPage = 1; // print each dive in a single page if the attribute is missing or malformed
|
|
|
|
//TODO: show warning
|
|
|
|
}
|
2021-02-13 18:58:14 +01:00
|
|
|
if (divesPerPage == 0)
|
2015-08-13 23:23:07 +02:00
|
|
|
flowRender();
|
2021-02-13 18:58:14 +01:00
|
|
|
else
|
|
|
|
render((t.numDives - 1) / divesPerPage + 1);
|
2015-04-21 18:05:20 +02:00
|
|
|
}
|
2015-07-10 21:45:27 +02:00
|
|
|
|
|
|
|
void Printer::previewOnePage()
|
|
|
|
{
|
|
|
|
if (printMode == PREVIEW) {
|
|
|
|
TemplateLayout t(printOptions, templateOptions);
|
|
|
|
|
|
|
|
pageSize.setHeight(paintDevice->height());
|
|
|
|
pageSize.setWidth(paintDevice->width());
|
|
|
|
webView->page()->setViewportSize(pageSize);
|
2015-08-21 10:56:59 +02:00
|
|
|
// initialize the border settings
|
2020-12-12 13:28:36 +01:00
|
|
|
// templateOptions.border_width = std::max(1, pageSize.width() / 1000);
|
|
|
|
if (printOptions.type == print_options::DIVELIST)
|
2021-02-13 22:43:55 +01:00
|
|
|
webView->setHtml(t.generate(inPlanner));
|
2020-12-12 13:28:36 +01:00
|
|
|
else if (printOptions.type == print_options::STATISTICS )
|
2015-08-21 19:12:33 +02:00
|
|
|
webView->setHtml(t.generateStatistics());
|
2015-08-21 11:02:10 +02:00
|
|
|
bool ok;
|
|
|
|
int divesPerPage = webView->page()->mainFrame()->findFirstElement("body").attribute("data-numberofdives").toInt(&ok);
|
|
|
|
if (!ok) {
|
|
|
|
divesPerPage = 1; // print each dive in a single page if the attribute is missing or malformed
|
|
|
|
//TODO: show warning
|
|
|
|
}
|
|
|
|
if (divesPerPage == 0) {
|
|
|
|
flowRender();
|
|
|
|
} else {
|
|
|
|
render(1);
|
|
|
|
}
|
2015-07-10 21:45:27 +02:00
|
|
|
}
|
|
|
|
}
|