mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Ink coverage calculator
This commit is contained in:
@ -3848,4 +3848,146 @@ void PDFTransparencyRenderer::PDFTransparencySoftMask::makeOpaque()
|
||||
}
|
||||
}
|
||||
|
||||
PDFInkCoverageCalculator::PDFInkCoverageCalculator(const PDFDocument* document,
|
||||
const PDFFontCache* fontCache,
|
||||
const PDFCMSManager* cmsManager,
|
||||
const PDFOptionalContentActivity* optionalContentActivity,
|
||||
const PDFInkMapper* inkMapper,
|
||||
PDFTransparencyRendererSettings settings) :
|
||||
m_document(document),
|
||||
m_fontCache(fontCache),
|
||||
m_cmsManager(cmsManager),
|
||||
m_optionalContentActivity(optionalContentActivity),
|
||||
m_inkMapper(inkMapper),
|
||||
m_settings(settings)
|
||||
{
|
||||
Q_ASSERT(m_document);
|
||||
Q_ASSERT(m_fontCache);
|
||||
Q_ASSERT(m_cmsManager);
|
||||
Q_ASSERT(m_optionalContentActivity);
|
||||
Q_ASSERT(m_inkMapper);
|
||||
}
|
||||
|
||||
void PDFInkCoverageCalculator::perform(QSize size, const std::vector<PDFInteger>& pages)
|
||||
{
|
||||
if (pages.empty())
|
||||
{
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
auto calculatePageCoverage = [this, size](PDFInteger pageIndex)
|
||||
{
|
||||
if (pageIndex >= PDFInteger(m_document->getCatalog()->getPageCount()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const PDFPage* page = m_document->getCatalog()->getPage(pageIndex);
|
||||
if (!page)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QRectF pageRect = page->getRotatedMediaBox();
|
||||
QSizeF pageSize = pageRect.size();
|
||||
pageSize.scale(size.width(), size.height(), Qt::KeepAspectRatio);
|
||||
QSize imageSize = pageSize.toSize();
|
||||
|
||||
if (!imageSize.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pdf::PDFTransparencyRendererSettings settings;
|
||||
settings.flags.setFlag(PDFTransparencyRendererSettings::SaveOriginalProcessImage, true);
|
||||
|
||||
// Jakub Melka: debug is very slow, use multithreading
|
||||
#ifdef QT_DEBUG
|
||||
settings.flags.setFlag(PDFTransparencyRendererSettings::MultithreadedPathSampler, true);
|
||||
#endif
|
||||
|
||||
settings.flags.setFlag(PDFTransparencyRendererSettings::ActiveColorMask, false);
|
||||
settings.flags.setFlag(PDFTransparencyRendererSettings::SeparationSimulation, true);
|
||||
settings.activeColorMask = PDFPixelFormat::getAllColorsMask();
|
||||
|
||||
QMatrix pagePointToDevicePoint = pdf::PDFRenderer::createPagePointToDevicePointMatrix(page, QRect(QPoint(0, 0), imageSize));
|
||||
pdf::PDFCMSPointer cms = m_cmsManager->getCurrentCMS();
|
||||
pdf::PDFTransparencyRenderer renderer(page, m_document, m_fontCache, cms.data(), m_optionalContentActivity,
|
||||
m_inkMapper, settings, pagePointToDevicePoint);
|
||||
|
||||
renderer.beginPaint(imageSize);
|
||||
renderer.processContents();
|
||||
renderer.endPaint();
|
||||
|
||||
PDFFloatBitmapWithColorSpace originalProcessImage = renderer.getOriginalProcessBitmap();
|
||||
QSizeF pageSizeMM = page->getRotatedMediaBoxMM().size();
|
||||
|
||||
pdf::PDFPixelFormat pixelFormat = originalProcessImage.getPixelFormat();
|
||||
pdf::PDFColorComponent totalArea = pageSizeMM.width() * pageSizeMM.height();
|
||||
pdf::PDFColorComponent pixelArea = totalArea / pdf::PDFColorComponent(originalProcessImage.getWidth() * originalProcessImage.getHeight());
|
||||
|
||||
std::vector<PDFColorComponent> pageCoverage;
|
||||
const uint8_t colorChannelCount = pixelFormat.getColorChannelCount();
|
||||
pageCoverage.resize(colorChannelCount, 0.0f);
|
||||
|
||||
for (size_t y = 0; y < originalProcessImage.getHeight(); ++y)
|
||||
{
|
||||
for (size_t x = 0; x < originalProcessImage.getWidth(); ++x)
|
||||
{
|
||||
const pdf::PDFColorBuffer buffer = originalProcessImage.getPixel(x, y);
|
||||
const pdf::PDFColorComponent alpha = pixelFormat.hasOpacityChannel() ? buffer[pixelFormat.getOpacityChannelIndex()] : 1.0f;
|
||||
|
||||
for (uint8_t i = 0; i < colorChannelCount; ++i)
|
||||
{
|
||||
pageCoverage[i] += buffer[i] * alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PDFColorComponent> pageRatioCoverage = pageCoverage;
|
||||
for (uint8_t i = 0; i < colorChannelCount; ++i)
|
||||
{
|
||||
pageCoverage[i] *= pixelArea;
|
||||
pageRatioCoverage[i] *= pixelArea / totalArea;
|
||||
}
|
||||
|
||||
std::vector<PDFInkMapper::ColorInfo> separations = m_inkMapper->getSeparations(pixelFormat.getProcessColorChannelCount());
|
||||
Q_ASSERT(pixelFormat.getColorChannelCount() == separations.size());
|
||||
|
||||
std::vector<InkCoverageChannelInfo> results;
|
||||
results.reserve(separations.size());
|
||||
|
||||
for (size_t i = 0; i < separations.size(); ++i)
|
||||
{
|
||||
const PDFInkMapper::ColorInfo& colorInfo = separations[i];
|
||||
|
||||
InkCoverageChannelInfo info;
|
||||
info.color = colorInfo.color;
|
||||
info.name = colorInfo.name;
|
||||
info.textName = colorInfo.textName;
|
||||
info.isSpot = colorInfo.isSpot;
|
||||
info.coveredArea = pageCoverage[i];
|
||||
results.emplace_back(qMove(info));
|
||||
}
|
||||
|
||||
QMutexLocker lock(&m_mutex);
|
||||
m_inkCoverageResults[pageIndex] = qMove(results);
|
||||
};
|
||||
|
||||
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, pages.begin(), pages.end(), calculatePageCoverage);
|
||||
}
|
||||
|
||||
const std::vector<PDFInkCoverageCalculator::InkCoverageChannelInfo>* PDFInkCoverageCalculator::getInkCoverage(PDFInteger pageIndex) const
|
||||
{
|
||||
auto it = m_inkCoverageResults.find(pageIndex);
|
||||
if (it != m_inkCoverageResults.end())
|
||||
{
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
static const std::vector<PDFInkCoverageCalculator::InkCoverageChannelInfo> dummy;
|
||||
return &dummy;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -924,6 +924,52 @@ private:
|
||||
PDFFloatBitmapWithColorSpace m_originalProcessBitmap;
|
||||
};
|
||||
|
||||
/// Ink coverage calculator. Calculates ink coverage for a given
|
||||
/// page range. Calculates ink coverage of both cmyk colors and spot colors.
|
||||
class Pdf4QtLIBSHARED_EXPORT PDFInkCoverageCalculator
|
||||
{
|
||||
public:
|
||||
PDFInkCoverageCalculator(const PDFDocument* document,
|
||||
const PDFFontCache* fontCache,
|
||||
const PDFCMSManager* cmsManager,
|
||||
const PDFOptionalContentActivity* optionalContentActivity,
|
||||
const PDFInkMapper* inkMapper,
|
||||
PDFTransparencyRendererSettings settings);
|
||||
|
||||
struct InkCoverageChannelInfo
|
||||
{
|
||||
QByteArray name;
|
||||
QString textName;
|
||||
bool isSpot = true;
|
||||
QColor color;
|
||||
PDFColorComponent coveredArea = 0.0f;
|
||||
PDFColorComponent ratio = 0.0f;
|
||||
};
|
||||
|
||||
/// Perform ink coverage calculations on given pages. Results are stored
|
||||
/// in this object. Page images are rendered using \p size resolution,
|
||||
/// and in this resolution, ink coverage is calculated.
|
||||
/// \param size Resolution size (for ink coverage calculation)
|
||||
/// \param pages Page indices
|
||||
void perform(QSize size, const std::vector<PDFInteger>& pages);
|
||||
|
||||
/// 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;
|
||||
|
||||
private:
|
||||
const PDFDocument* m_document;
|
||||
const PDFFontCache* m_fontCache;
|
||||
const PDFCMSManager* m_cmsManager;
|
||||
const PDFOptionalContentActivity* m_optionalContentActivity;
|
||||
const PDFInkMapper* m_inkMapper;
|
||||
PDFTransparencyRendererSettings m_settings;
|
||||
|
||||
QMutex m_mutex;
|
||||
std::map<pdf::PDFInteger, std::vector<InkCoverageChannelInfo>> m_inkCoverageResults;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFTRANSPARENCYRENDERER_H
|
||||
|
Reference in New Issue
Block a user