mirror of https://github.com/JakubMelka/PDF4QT.git
403 lines
12 KiB
C++
403 lines
12 KiB
C++
// Copyright (C) 2021 Jakub Melka
|
|
//
|
|
// This file is part of PDF4QT.
|
|
//
|
|
// PDF4QT is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// with the written consent of the copyright owner, any later version.
|
|
//
|
|
// PDF4QT 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 Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
#include "inkcoveragedialog.h"
|
|
#include "ui_inkcoveragedialog.h"
|
|
|
|
#include "pdfdrawspacecontroller.h"
|
|
#include "pdfwidgetutils.h"
|
|
|
|
#include <QCloseEvent>
|
|
#include <QtConcurrent/QtConcurrent>
|
|
|
|
namespace pdfplugin
|
|
{
|
|
|
|
InkCoverageDialog::InkCoverageDialog(const pdf::PDFDocument* document, pdf::PDFWidget* widget, QWidget* parent) :
|
|
QDialog(parent, Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint),
|
|
ui(new Ui::InkCoverageDialog),
|
|
m_inkMapper(widget->getCMSManager(), document),
|
|
m_document(document),
|
|
m_widget(widget),
|
|
m_model(nullptr),
|
|
m_futureWatcher(nullptr)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
m_inkMapper.createSpotColors(true);
|
|
|
|
m_model = new InkCoverageStatisticsModel(this);
|
|
ui->coverageTableView->setModel(m_model);
|
|
ui->coverageTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
|
|
|
setMinimumSize(pdf::PDFWidgetUtils::scaleDPI(this, QSize(800, 600)));
|
|
updateInkCoverage();
|
|
pdf::PDFWidgetUtils::style(this);
|
|
}
|
|
|
|
InkCoverageDialog::~InkCoverageDialog()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void InkCoverageDialog::updateInkCoverage()
|
|
{
|
|
Q_ASSERT(!m_future.isRunning());
|
|
Q_ASSERT(!m_futureWatcher);
|
|
|
|
auto calculateInkCoverage = [this]() -> InkCoverageResults
|
|
{
|
|
InkCoverageResults results;
|
|
|
|
pdf::PDFTransparencyRendererSettings settings;
|
|
|
|
// Jakub Melka: debug is very slow, use multithreading
|
|
#ifdef QT_DEBUG
|
|
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::MultithreadedPathSampler, true);
|
|
#endif
|
|
|
|
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::ActiveColorMask, false);
|
|
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::SeparationSimulation, m_inkMapper.getActiveSpotColorCount() > 0);
|
|
settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::SaveOriginalProcessImage, true);
|
|
|
|
pdf::PDFInkCoverageCalculator calculator(m_document,
|
|
m_widget->getDrawWidgetProxy()->getFontCache(),
|
|
m_widget->getCMSManager(),
|
|
m_widget->getDrawWidgetProxy()->getOptionalContentActivity(),
|
|
&m_inkMapper,
|
|
m_widget->getDrawWidgetProxy()->getProgress(),
|
|
settings);
|
|
|
|
std::vector<pdf::PDFInteger> pageIndices;
|
|
pageIndices.resize(m_document->getCatalog()->getPageCount(), 0);
|
|
std::iota(pageIndices.begin(), pageIndices.end(), 0);
|
|
calculator.perform(QSize(RESOLUTION, RESOLUTION), pageIndices);
|
|
|
|
CoverageInfo coverageInfo;
|
|
|
|
for (const pdf::PDFInteger pageIndex : pageIndices)
|
|
{
|
|
const std::vector<pdf::PDFInkCoverageCalculator::InkCoverageChannelInfo>* coverage = calculator.getInkCoverage(pageIndex);
|
|
|
|
if (!coverage)
|
|
{
|
|
// Jakub Melka: Something bad happened?
|
|
continue;
|
|
}
|
|
|
|
for (const auto& info : *coverage)
|
|
{
|
|
if (!pdf::PDFInkCoverageCalculator::findCoverageInfoByName(coverageInfo, info.name))
|
|
{
|
|
coverageInfo.push_back(info);
|
|
coverageInfo.back().coveredArea = 0.0;
|
|
coverageInfo.back().ratio = 0.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
CoverageInfo templateInfo = coverageInfo;
|
|
CoverageInfo sumInfo = coverageInfo;
|
|
|
|
for (const pdf::PDFInteger pageIndex : pageIndices)
|
|
{
|
|
const std::vector<pdf::PDFInkCoverageCalculator::InkCoverageChannelInfo>* coverage = calculator.getInkCoverage(pageIndex);
|
|
results.pageInfo.push_back(templateInfo);
|
|
|
|
if (!coverage)
|
|
{
|
|
// Jakub Melka: Something bad happened?
|
|
continue;
|
|
}
|
|
|
|
CoverageInfo& currentInfo = results.pageInfo.back();
|
|
|
|
for (const auto& info : *coverage)
|
|
{
|
|
pdf::PDFInkCoverageCalculator::InkCoverageChannelInfo* channelInfo = pdf::PDFInkCoverageCalculator::findCoverageInfoByName(currentInfo, info.name);
|
|
pdf::PDFInkCoverageCalculator::InkCoverageChannelInfo* sumChannelInfo = pdf::PDFInkCoverageCalculator::findCoverageInfoByName(sumInfo, info.name);
|
|
channelInfo->coveredArea = info.coveredArea;
|
|
channelInfo->ratio = info.ratio;
|
|
sumChannelInfo->coveredArea += info.coveredArea;
|
|
}
|
|
}
|
|
|
|
results.sumInfo = qMove(sumInfo);
|
|
|
|
return results;
|
|
};
|
|
|
|
m_future = QtConcurrent::run(calculateInkCoverage);
|
|
m_futureWatcher = new QFutureWatcher<InkCoverageResults>();
|
|
connect(m_futureWatcher, &QFutureWatcher<InkCoverageResults>::finished, this, &InkCoverageDialog::onInkCoverageCalculated);
|
|
m_futureWatcher->setFuture(m_future);
|
|
}
|
|
|
|
void InkCoverageDialog::closeEvent(QCloseEvent* event)
|
|
{
|
|
if (!isInkCoverageCalculated())
|
|
{
|
|
event->ignore();
|
|
return;
|
|
}
|
|
|
|
BaseClass::closeEvent(event);
|
|
}
|
|
|
|
void InkCoverageDialog::accept()
|
|
{
|
|
if (!isInkCoverageCalculated())
|
|
{
|
|
return;
|
|
}
|
|
|
|
BaseClass::accept();
|
|
}
|
|
|
|
void InkCoverageDialog::reject()
|
|
{
|
|
if (!isInkCoverageCalculated())
|
|
{
|
|
return;
|
|
}
|
|
|
|
BaseClass::reject();
|
|
}
|
|
|
|
void InkCoverageDialog::onInkCoverageCalculated()
|
|
{
|
|
Q_ASSERT(m_future.isFinished());
|
|
InkCoverageResults results = m_future.result();
|
|
m_model->setInkCoverageResults(qMove(results));
|
|
}
|
|
|
|
bool InkCoverageDialog::isInkCoverageCalculated() const
|
|
{
|
|
return !(m_futureWatcher && m_futureWatcher->isRunning());
|
|
}
|
|
|
|
InkCoverageStatisticsModel::InkCoverageStatisticsModel(QObject* parent) :
|
|
BaseClass(parent)
|
|
{
|
|
|
|
}
|
|
|
|
QModelIndex InkCoverageStatisticsModel::index(int row, int column, const QModelIndex& parent) const
|
|
{
|
|
Q_UNUSED(parent);
|
|
return createIndex(row, column, nullptr);
|
|
}
|
|
|
|
QModelIndex InkCoverageStatisticsModel::parent(const QModelIndex& child) const
|
|
{
|
|
Q_UNUSED(child);
|
|
|
|
return QModelIndex();
|
|
}
|
|
|
|
int InkCoverageStatisticsModel::rowCount(const QModelIndex& parent) const
|
|
{
|
|
Q_UNUSED(parent);
|
|
|
|
if (m_inkCoverageResults.pageInfo.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return int(m_inkCoverageResults.pageInfo.size() + 1);
|
|
}
|
|
|
|
int InkCoverageStatisticsModel::columnCount(const QModelIndex& parent) const
|
|
{
|
|
Q_UNUSED(parent);
|
|
|
|
return LastStandardColumn + getChannelCount() * LastChannelColumn;
|
|
}
|
|
|
|
QVariant InkCoverageStatisticsModel::data(const QModelIndex& index, int role) const
|
|
{
|
|
if (!index.isValid())
|
|
{
|
|
return QVariant();
|
|
}
|
|
|
|
switch (role)
|
|
{
|
|
case Qt::DisplayRole:
|
|
{
|
|
const int row = index.row();
|
|
QLocale locale;
|
|
|
|
const bool isTotalRow = row == rowCount(index.parent()) - 1;
|
|
if (index.column() == StandardColumnPageIndex)
|
|
{
|
|
if (isTotalRow)
|
|
{
|
|
return tr("Total");
|
|
}
|
|
return locale.toString(row + 1);
|
|
}
|
|
|
|
Q_ASSERT(index.column() >= LastStandardColumn);
|
|
|
|
const CoverageInfo* info = nullptr;
|
|
if (row < m_inkCoverageResults.pageInfo.size())
|
|
{
|
|
info = &m_inkCoverageResults.pageInfo[row];
|
|
}
|
|
else
|
|
{
|
|
info = &m_inkCoverageResults.sumInfo;
|
|
}
|
|
|
|
const int channelIndex = getChannelIndex(index.column());
|
|
EChannelColumn channelColumn = getChannelColumn(index.column());
|
|
ChannelCoverageInfo channelInfo = info->at(channelIndex);
|
|
|
|
switch (channelColumn)
|
|
{
|
|
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnColorant:
|
|
return QString();
|
|
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnCoverageArea:
|
|
return locale.toString(channelInfo.coveredArea, 'f', 2);
|
|
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnCoverageRatio:
|
|
return !isTotalRow ? locale.toString(channelInfo.ratio * 100.0, 'f', 0) : QString();
|
|
|
|
default:
|
|
Q_ASSERT(false);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Qt::TextAlignmentRole:
|
|
{
|
|
if (index.column() == StandardColumnPageIndex)
|
|
{
|
|
return int(Qt::AlignCenter);
|
|
}
|
|
|
|
Q_ASSERT(index.column() >= LastStandardColumn);
|
|
|
|
EChannelColumn channelColumn = getChannelColumn(index.column());
|
|
switch (channelColumn)
|
|
{
|
|
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnColorant:
|
|
return int(Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnCoverageArea:
|
|
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnCoverageRatio:
|
|
return int(Qt::AlignRight | Qt::AlignVCenter);
|
|
|
|
default:
|
|
Q_ASSERT(false);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Qt::BackgroundRole:
|
|
{
|
|
if (index.column() >= LastStandardColumn && getChannelColumn(index.column()) == ChannelColumnColorant)
|
|
{
|
|
const int channelIndex = getChannelIndex(index.column());
|
|
return QBrush(m_inkCoverageResults.sumInfo[channelIndex].color);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
QVariant InkCoverageStatisticsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
|
|
{
|
|
return QVariant();
|
|
}
|
|
|
|
if (section < LastStandardColumn)
|
|
{
|
|
Q_ASSERT(section == StandardColumnPageIndex);
|
|
return tr("Page Index");
|
|
}
|
|
|
|
const EChannelColumn channelColumn = getChannelColumn(section);
|
|
|
|
switch (channelColumn)
|
|
{
|
|
case ChannelColumnColorant:
|
|
{
|
|
const int channelIndex = getChannelIndex(section);
|
|
return m_inkCoverageResults.sumInfo[channelIndex].textName;
|
|
}
|
|
|
|
case ChannelColumnCoverageArea:
|
|
return tr("[ mm² ]");
|
|
|
|
case ChannelColumnCoverageRatio:
|
|
return tr("[ % ]");
|
|
|
|
case LastChannelColumn:
|
|
Q_ASSERT(false);
|
|
break;
|
|
|
|
default:
|
|
Q_ASSERT(false);
|
|
break;
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
void InkCoverageStatisticsModel::setInkCoverageResults(InkCoverageResults inkCoverageResults)
|
|
{
|
|
beginResetModel();
|
|
m_inkCoverageResults = qMove(inkCoverageResults);
|
|
endResetModel();
|
|
}
|
|
|
|
int InkCoverageStatisticsModel::getChannelCount() const
|
|
{
|
|
return int(m_inkCoverageResults.sumInfo.size());
|
|
}
|
|
|
|
InkCoverageStatisticsModel::EChannelColumn InkCoverageStatisticsModel::getChannelColumn(int index) const
|
|
{
|
|
Q_ASSERT(index >= LastStandardColumn);
|
|
|
|
index -= LastStandardColumn;
|
|
return static_cast<EChannelColumn>(index % LastChannelColumn);
|
|
}
|
|
|
|
int InkCoverageStatisticsModel::getChannelIndex(int index) const
|
|
{
|
|
Q_ASSERT(index >= LastStandardColumn);
|
|
|
|
index -= LastStandardColumn;
|
|
return index / LastChannelColumn;
|
|
}
|
|
|
|
} // pdfplugin
|