Ink coverage dialog

This commit is contained in:
Jakub Melka 2021-04-20 19:33:54 +02:00
parent cfedca6f4f
commit d8b5eed3a9
12 changed files with 829 additions and 5 deletions

View File

@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Jakub Melka
// Copyright (C) 2019-2021 Jakub Melka
//
// This file is part of Pdf4Qt.
//
@ -54,7 +54,7 @@ void PDFProgress::step()
if (emitSignal)
{
progressStep(newPercentage);
emit progressStep(newPercentage);
}
}

View File

@ -3853,12 +3853,14 @@ PDFInkCoverageCalculator::PDFInkCoverageCalculator(const PDFDocument* document,
const PDFCMSManager* cmsManager,
const PDFOptionalContentActivity* optionalContentActivity,
const PDFInkMapper* inkMapper,
PDFProgress* progress,
PDFTransparencyRendererSettings settings) :
m_document(document),
m_fontCache(fontCache),
m_cmsManager(cmsManager),
m_optionalContentActivity(optionalContentActivity),
m_inkMapper(inkMapper),
m_progress(progress),
m_settings(settings)
{
Q_ASSERT(m_document);
@ -3876,6 +3878,11 @@ void PDFInkCoverageCalculator::perform(QSize size, const std::vector<PDFInteger>
return;
}
if (m_progress)
{
m_progress->start(pages.size(), ProgressStartupInfo());
}
auto calculatePageCoverage = [this, size](PDFInteger pageIndex)
{
if (pageIndex >= PDFInteger(m_document->getCatalog()->getPageCount()))
@ -3971,11 +3978,27 @@ void PDFInkCoverageCalculator::perform(QSize size, const std::vector<PDFInteger>
results.emplace_back(qMove(info));
}
if (m_progress)
{
m_progress->step();
}
QMutexLocker lock(&m_mutex);
m_inkCoverageResults[pageIndex] = qMove(results);
};
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, pages.begin(), pages.end(), calculatePageCoverage);
if (m_progress)
{
m_progress->finish();
}
}
void PDFInkCoverageCalculator::clear()
{
QMutexLocker lock(&m_mutex);
m_inkCoverageResults.clear();
}
const std::vector<PDFInkCoverageCalculator::InkCoverageChannelInfo>* PDFInkCoverageCalculator::getInkCoverage(PDFInteger pageIndex) const
@ -3990,4 +4013,27 @@ const std::vector<PDFInkCoverageCalculator::InkCoverageChannelInfo>* PDFInkCover
return &dummy;
}
const PDFInkCoverageCalculator::InkCoverageChannelInfo* PDFInkCoverageCalculator::findCoverageInfoByName(const std::vector<PDFInkCoverageCalculator::InkCoverageChannelInfo>& infos,
const QByteArray& name)
{
auto it = std::find_if(infos.cbegin(), infos.cend(), [&name](const auto& info) { return info.name == name; });
if (it != infos.cend())
{
return &*it;
}
return nullptr;
}
PDFInkCoverageCalculator::InkCoverageChannelInfo* PDFInkCoverageCalculator::findCoverageInfoByName(std::vector<PDFInkCoverageCalculator::InkCoverageChannelInfo>& infos, const QByteArray& name)
{
auto it = std::find_if(infos.begin(), infos.end(), [&name](const auto& info) { return info.name == name; });
if (it != infos.cend())
{
return &*it;
}
return nullptr;
}
} // namespace pdf

View File

@ -23,6 +23,7 @@
#include "pdfpagecontentprocessor.h"
#include "pdfconstants.h"
#include "pdfutils.h"
#include "pdfprogress.h"
#include <QImage>
@ -934,6 +935,7 @@ public:
const PDFCMSManager* cmsManager,
const PDFOptionalContentActivity* optionalContentActivity,
const PDFInkMapper* inkMapper,
PDFProgress* progress,
PDFTransparencyRendererSettings settings);
struct InkCoverageChannelInfo
@ -953,17 +955,35 @@ public:
/// \param pages Page indices
void perform(QSize size, const std::vector<PDFInteger>& pages);
/// Clear all calculated ink coverage results
void clear();
/// Clears calculated ink coverage for all pages. If ink coverage is not
/// calculated for given page index, empty vector is returned.
/// \param pageIndex Page index
const std::vector<InkCoverageChannelInfo>* getInkCoverage(PDFInteger pageIndex) const;
/// Find coverage info in vector by colorant name. If coverage info with a given colorant
/// name is not found, then nullptr is returned.
/// \param infos Vector of coverage info
/// \param name Colornant name
/// \returns Info for a given colorant name, or nullptr, if it is not found
static const InkCoverageChannelInfo* findCoverageInfoByName(const std::vector<InkCoverageChannelInfo>& infos, const QByteArray& name);
/// Find coverage info in vector by colorant name. If coverage info with a given colorant
/// name is not found, then nullptr is returned.
/// \param infos Vector of coverage info
/// \param name Colornant name
/// \returns Info for a given colorant name, or nullptr, if it is not found
static InkCoverageChannelInfo* findCoverageInfoByName(std::vector<InkCoverageChannelInfo>& infos, const QByteArray& name);
private:
const PDFDocument* m_document;
const PDFFontCache* m_fontCache;
const PDFCMSManager* m_cmsManager;
const PDFOptionalContentActivity* m_optionalContentActivity;
const PDFInkMapper* m_inkMapper;
PDFProgress* m_progress;
PDFTransparencyRendererSettings m_settings;
QMutex m_mutex;

View File

@ -3,5 +3,5 @@
"Author" : "Jakub Melka",
"Version" : "1.0.0",
"License" : "LGPL v3",
"Description" : "View prepress output preview (overprint, spot colors, advanced transparency)."
"Description" : "View prepress output preview (overprint, spot colors, advanced transparency). Measure ink coverage for process and spot colors for all pages."
}

View File

@ -33,11 +33,13 @@ DESTDIR = $$OUT_PWD/../../pdfplugins
CONFIG += c++11
SOURCES += \
inkcoveragedialog.cpp \
outputpreviewdialog.cpp \
outputpreviewplugin.cpp \
outputpreviewwidget.cpp
HEADERS += \
inkcoveragedialog.h \
outputpreviewdialog.h \
outputpreviewplugin.h \
outputpreviewwidget.h
@ -51,4 +53,5 @@ RESOURCES += \
icons.qrc
FORMS += \
inkcoveragedialog.ui \
outputpreviewdialog.ui

View File

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/pdfplugins/outputpreview">
<file>preview.svg</file>
<file>ink-coverage.svg</file>
</qresource>
</RCC>

View File

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="30mm"
height="30mm"
viewBox="0 0 30 30"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="ink-coverage.svg"
enable-background="new">
<defs
id="defs2">
<inkscape:path-effect
effect="spiro"
id="path-effect831"
is_visible="true" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.959798"
inkscape:cx="353.90161"
inkscape:cy="22.898892"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="3840"
inkscape:window-height="2035"
inkscape:window-x="-13"
inkscape:window-y="-13"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
<dc:creator>
<cc:Agent>
<dc:title>Jakub Melka</dc:title>
</cc:Agent>
</dc:creator>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Vrstva 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-267)">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.06046581;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect4534"
width="23.724977"
height="11.795169"
x="3.0676022"
y="281.75931"
ry="0.0470865" />
<path
sodipodi:type="spiral"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332"
id="path4536"
sodipodi:cx="6.8319197"
sodipodi:cy="287.80103"
sodipodi:expansion="1"
sodipodi:revolution="3"
sodipodi:radius="2.2576218"
sodipodi:argument="-18.617268"
sodipodi:t0="0"
d="m 6.8319197,287.80103 c 0.1101534,0.026 0.01922,0.16134 -0.04331,0.18308 -0.1694529,0.0589 -0.3112585,-0.11718 -0.3228548,-0.2697 -0.020743,-0.27284 0.2420036,-0.46852 0.4960934,-0.46263 0.372887,0.009 0.6288465,0.36795 0.6024,0.72248 -0.035249,0.47254 -0.494221,0.79039 -0.9488772,0.74218 -0.5721383,-0.0607 -0.9525297,-0.62063 -0.8819452,-1.17527 0.085487,-0.67175 0.7471009,-1.11503 1.401661,-1.02172 0.7713764,0.10996 1.2777336,0.87362 1.1614904,1.62805 -0.1342073,0.87102 -1.0001713,1.44059 -1.8544448,1.30127 -0.9706798,-0.15832 -1.6035477,-1.12675 -1.4410356,-2.08084 0.1823141,-1.07035 1.2533288,-1.76658 2.3072286,-1.58081 1.1700285,0.20624 1.9296674,1.37993 1.7205808,2.53362"
transform="matrix(1.5963587,0,0,1.64111,-3.4226782,-184.59737)" />
<path
sodipodi:type="spiral"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332"
id="path4536-3"
sodipodi:cx="6.8319197"
sodipodi:cy="287.80103"
sodipodi:expansion="1"
sodipodi:revolution="3"
sodipodi:radius="2.2576218"
sodipodi:argument="-18.617268"
sodipodi:t0="0"
d="m 6.8319197,287.80103 c 0.1101534,0.026 0.01922,0.16134 -0.04331,0.18308 -0.1694529,0.0589 -0.3112585,-0.11718 -0.3228548,-0.2697 -0.020743,-0.27284 0.2420036,-0.46852 0.4960934,-0.46263 0.372887,0.009 0.6288465,0.36795 0.6024,0.72248 -0.035249,0.47254 -0.494221,0.79039 -0.9488772,0.74218 -0.5721383,-0.0607 -0.9525297,-0.62063 -0.8819452,-1.17527 0.085487,-0.67175 0.7471009,-1.11503 1.401661,-1.02172 0.7713764,0.10996 1.2777336,0.87362 1.1614904,1.62805 -0.1342073,0.87102 -1.0001713,1.44059 -1.8544448,1.30127 -0.9706798,-0.15832 -1.6035477,-1.12675 -1.4410356,-2.08084 0.1823141,-1.07035 1.2533288,-1.76658 2.3072286,-1.58081 1.1700285,0.20624 1.9296674,1.37993 1.7205808,2.53362"
transform="matrix(1.5963587,0,0,1.64111,4.1022508,-184.64367)" />
<path
sodipodi:type="spiral"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332"
id="path4536-6"
sodipodi:cx="6.8319197"
sodipodi:cy="287.80103"
sodipodi:expansion="1"
sodipodi:revolution="3"
sodipodi:radius="2.2576218"
sodipodi:argument="-18.617268"
sodipodi:t0="0"
d="m 6.8319197,287.80103 c 0.1101534,0.026 0.01922,0.16134 -0.04331,0.18308 -0.1694529,0.0589 -0.3112585,-0.11718 -0.3228548,-0.2697 -0.020743,-0.27284 0.2420036,-0.46852 0.4960934,-0.46263 0.372887,0.009 0.6288465,0.36795 0.6024,0.72248 -0.035249,0.47254 -0.494221,0.79039 -0.9488772,0.74218 -0.5721383,-0.0607 -0.9525297,-0.62063 -0.8819452,-1.17527 0.085487,-0.67175 0.7471009,-1.11503 1.401661,-1.02172 0.7713764,0.10996 1.2777336,0.87362 1.1614904,1.62805 -0.1342073,0.87102 -1.0001713,1.44059 -1.8544448,1.30127 -0.9706798,-0.15832 -1.6035477,-1.12675 -1.4410356,-2.08084 0.1823141,-1.07035 1.2533288,-1.76658 2.3072286,-1.58081 1.1700285,0.20624 1.9296674,1.37993 1.7205808,2.53362"
transform="matrix(1.5963587,0,0,1.64111,11.378292,-184.69091)" />
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect4559"
width="10.924642"
height="6.8821898"
x="9.9223804"
y="274.85004"
ry="0.047246911" />
<rect
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect4561"
width="15.167545"
height="3.3742776"
x="8.0849028"
y="271.40894"
ry="0.047246911" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -0,0 +1,381 @@
// 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
// (at your option) 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();
}
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);
channelInfo->coveredArea = info.coveredArea;
channelInfo->ratio = info.ratio;
}
}
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;
if (index.column() == StandardColumnPageIndex)
{
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 channelInfo.coveredArea > 0.0 ? channelInfo.textName : QString();
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnCoverageArea:
return locale.toString(channelInfo.coveredArea, 'f', 2);
case pdfplugin::InkCoverageStatisticsModel::ChannelColumnCoverageRatio:
return locale.toString(channelInfo.ratio * 100.0, 'f', 0);
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;
}
}
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

View File

@ -0,0 +1,123 @@
// 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
// (at your option) 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/>.
#ifndef INKCOVERAGEDIALOG_H
#define INKCOVERAGEDIALOG_H
#include "pdfdocument.h"
#include "pdfdrawwidget.h"
#include "pdftransparencyrenderer.h"
#include <QDialog>
#include <QAbstractItemModel>
namespace Ui
{
class InkCoverageDialog;
}
namespace pdfplugin
{
class InkCoverageStatisticsModel;
using ChannelCoverageInfo = pdf::PDFInkCoverageCalculator::InkCoverageChannelInfo;
using CoverageInfo = std::vector<ChannelCoverageInfo>;
struct InkCoverageResults
{
std::vector<CoverageInfo> pageInfo;
CoverageInfo sumInfo;
};
class InkCoverageDialog : public QDialog
{
Q_OBJECT
private:
using BaseClass = QDialog;
public:
explicit InkCoverageDialog(const pdf::PDFDocument* document, pdf::PDFWidget* widget, QWidget* parent);
virtual ~InkCoverageDialog() override;
void updateInkCoverage();
virtual void closeEvent(QCloseEvent* event) override;
virtual void accept() override;
virtual void reject() override;
private:
Ui::InkCoverageDialog* ui;
static constexpr int RESOLUTION = 1920;
void onInkCoverageCalculated();
bool isInkCoverageCalculated() const;
pdf::PDFInkMapper m_inkMapper;
const pdf::PDFDocument* m_document;
pdf::PDFWidget* m_widget;
InkCoverageStatisticsModel* m_model;
QFuture<InkCoverageResults> m_future;
QFutureWatcher<InkCoverageResults>* m_futureWatcher;
};
class InkCoverageStatisticsModel : public QAbstractItemModel
{
Q_OBJECT
private:
using BaseClass = QAbstractItemModel;
public:
explicit InkCoverageStatisticsModel(QObject* parent);
enum EStandardColumn
{
StandardColumnPageIndex,
LastStandardColumn
};
enum EChannelColumn
{
ChannelColumnColorant,
ChannelColumnCoverageArea,
ChannelColumnCoverageRatio,
LastChannelColumn
};
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
virtual QModelIndex parent(const QModelIndex& child) const override;
virtual int rowCount(const QModelIndex& parent) const override;
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
void setInkCoverageResults(InkCoverageResults inkCoverageResults);
int getChannelCount() const;
EChannelColumn getChannelColumn(int index) const;
int getChannelIndex(int index) const;
private:
InkCoverageResults m_inkCoverageResults;
};
} // namespace pdfplugin
#endif // INKCOVERAGEDIALOG_H

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InkCoverageDialog</class>
<widget class="QDialog" name="InkCoverageDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>715</width>
<height>551</height>
</rect>
</property>
<property name="windowTitle">
<string>Ink Coverage Calculator</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="coverageLabel">
<property name="text">
<string>Ink Coverage</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="coverageTableView">
<attribute name="horizontalHeaderMinimumSectionSize">
<number>40</number>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>InkCoverageDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>InkCoverageDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -17,6 +17,7 @@
#include "outputpreviewplugin.h"
#include "outputpreviewdialog.h"
#include "inkcoveragedialog.h"
#include "pdfdrawwidget.h"
#include <QAction>
@ -26,7 +27,8 @@ namespace pdfplugin
OutputPreviewPlugin::OutputPreviewPlugin() :
pdf::PDFPlugin(nullptr),
m_outputPreviewAction(nullptr)
m_outputPreviewAction(nullptr),
m_inkCoverageAction(nullptr)
{
}
@ -39,8 +41,11 @@ void OutputPreviewPlugin::setWidget(pdf::PDFWidget* widget)
m_outputPreviewAction = new QAction(QIcon(":/pdfplugins/outputpreview/preview.svg"), tr("Output Preview"), this);
m_outputPreviewAction->setObjectName("actionOutputPreview_OutputPreview");
m_inkCoverageAction = new QAction(QIcon(":/pdfplugins/outputpreview/ink-coverage.svg"), tr("Ink Coverage"), this);
m_inkCoverageAction->setObjectName("actionOutputPreview_InkCoverage");
connect(m_outputPreviewAction, &QAction::triggered, this, &OutputPreviewPlugin::onOutputPreviewTriggered);
connect(m_inkCoverageAction, &QAction::triggered, this, &OutputPreviewPlugin::onInkCoverageTriggered);
updateActions();
}
@ -57,7 +62,7 @@ void OutputPreviewPlugin::setDocument(const pdf::PDFModifiedDocument& document)
std::vector<QAction*> OutputPreviewPlugin::getActions() const
{
return { m_outputPreviewAction };
return { m_outputPreviewAction, m_inkCoverageAction };
}
void OutputPreviewPlugin::onOutputPreviewTriggered()
@ -66,9 +71,16 @@ void OutputPreviewPlugin::onOutputPreviewTriggered()
dialog.exec();
}
void OutputPreviewPlugin::onInkCoverageTriggered()
{
InkCoverageDialog dialog(m_document, m_widget, m_widget);
dialog.exec();
}
void OutputPreviewPlugin::updateActions()
{
m_outputPreviewAction->setEnabled(m_widget && m_document);
m_inkCoverageAction->setEnabled(m_widget && m_document);
}
}

View File

@ -42,9 +42,11 @@ public:
private:
void onOutputPreviewTriggered();
void onInkCoverageTriggered();
void updateActions();
QAction* m_outputPreviewAction;
QAction* m_inkCoverageAction;
};
} // namespace pdfplugin