Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 106 additions & 31 deletions LoopLibCore/sources/pdfinkcoverageprobe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,52 @@ QRectF resolveAnalysisBox(const PDFPage* page, PDFInkCoverageAnalysisBox request
return media;
}

/// Device raster of an analysis box at a resolution. An empty size means the box
/// cannot be rasterized.
struct CompositorRaster
{
QSize size;
QTransform pagePointToDevice;
bool budgetExceeded = false;
};

CompositorRaster planCompositorRaster(const PDFPage* page, const QRectF& analysisBox, int dpi, qint64 maxRasterPixels)
{
CompositorRaster raster;
const PageRotation pageRotation = page->getPageRotation();
const QRectF rotatedAnalysisBox = PDFPage::getRotatedBox(analysisBox, pageRotation).normalized();
const qreal pointToPixel = static_cast<qreal>(dpi) / 72.0;
const double widthReal = std::ceil(rotatedAnalysisBox.width() * pointToPixel);
const double heightReal = std::ceil(rotatedAnalysisBox.height() * pointToPixel);

if (!std::isfinite(widthReal) || !std::isfinite(heightReal) || widthReal <= 0.0 || heightReal <= 0.0 || widthReal > static_cast<double>(std::numeric_limits<int>::max()) || heightReal > static_cast<double>(std::numeric_limits<int>::max()))
{
return raster;
}

const int width = qMax(1, static_cast<int>(widthReal));
const int height = qMax(1, static_cast<int>(heightReal));
if (maxRasterPixels > 0 && static_cast<qint64>(width) * static_cast<qint64>(height) > maxRasterPixels)
{
raster.budgetExceeded = true;
return raster;
}

raster.size = QSize(width, height);
raster.pagePointToDevice = PDFRenderer::createMediaBoxToDevicePointMatrix(rotatedAnalysisBox, QRect(QPoint(0, 0), raster.size), pageRotation);
return raster;
}

PDFTransparencyRendererSettings compositorRendererSettings()
{
PDFTransparencyRendererSettings rendererSettings;
rendererSettings.flags.setFlag(PDFTransparencyRendererSettings::ActiveColorMask, false);
rendererSettings.flags.setFlag(PDFTransparencyRendererSettings::SeparationSimulation, true);
rendererSettings.activeColorMask = PDFPixelFormat::getAllColorsMask();
rendererSettings.renderPolicy = PDFRenderPolicy::forPreflightAnalysis();
return rendererSettings;
}

} // namespace

PDFInkCoverageProbe::PDFInkCoverageProbe(PDFDocumentSession* session) :
Expand Down Expand Up @@ -114,39 +160,15 @@ PDFInkCoverageProbeResult PDFInkCoverageProbe::probe(const PDFPage* page,
return result;
}

const PageRotation pageRotation = page->getPageRotation();
const QRectF rotatedAnalysisBox = PDFPage::getRotatedBox(analysisBox, pageRotation).normalized();
const QSizeF mediaSize = rotatedAnalysisBox.size();
const qreal pointToPixel = static_cast<qreal>(settings.dpi) / 72.0;
const double widthReal = std::ceil(mediaSize.width() * pointToPixel);
const double heightReal = std::ceil(mediaSize.height() * pointToPixel);

if (!std::isfinite(widthReal) || !std::isfinite(heightReal) || widthReal <= 0.0 || heightReal <= 0.0 || widthReal > static_cast<double>(std::numeric_limits<int>::max()) || heightReal > static_cast<double>(std::numeric_limits<int>::max()))
{
return result;
}

const int width = qMax(1, static_cast<int>(widthReal));
const int height = qMax(1, static_cast<int>(heightReal));
const qint64 rasterPixels = static_cast<qint64>(width) * static_cast<qint64>(height);
if (settings.maxRasterPixels > 0 && rasterPixels > settings.maxRasterPixels)
const CompositorRaster raster = planCompositorRaster(page, analysisBox, settings.dpi, settings.maxRasterPixels);
result.budgetExceeded = raster.budgetExceeded;
if (raster.size.isEmpty())
{
result.budgetExceeded = true;
return result;
}

PDFTransparencyRendererSettings rendererSettings;
PDFTransparencyRendererSettings rendererSettings = compositorRendererSettings();
rendererSettings.flags.setFlag(PDFTransparencyRendererSettings::SaveOriginalProcessImage, true);
rendererSettings.flags.setFlag(PDFTransparencyRendererSettings::ActiveColorMask, false);
rendererSettings.flags.setFlag(PDFTransparencyRendererSettings::SeparationSimulation, true);
rendererSettings.activeColorMask = PDFPixelFormat::getAllColorsMask();
rendererSettings.renderPolicy = PDFRenderPolicy::forPreflightAnalysis();

const QSize imageSize(width, height);
const QTransform pagePointToDevice = PDFRenderer::createMediaBoxToDevicePointMatrix(
rotatedAnalysisBox,
QRect(QPoint(0, 0), imageSize),
pageRotation);
PDFInkMapper inkMapper(nullptr, document);
inkMapper.createSpotColors(true);

Expand All @@ -157,8 +179,8 @@ PDFInkCoverageProbeResult PDFInkCoverageProbe::probe(const PDFPage* page,
m_session->getOptionalContentActivity(),
&inkMapper,
rendererSettings,
pagePointToDevice);
renderer.beginPaint(imageSize);
raster.pagePointToDevice);
renderer.beginPaint(raster.size);
renderer.processContents();
renderer.endPaint();
result.diagnostics = renderer.getRenderDiagnostics();
Expand Down Expand Up @@ -269,7 +291,7 @@ PDFInkCoverageProbeResult PDFInkCoverageProbe::probe(const PDFPage* page,
}

bool invertible = false;
const QTransform deviceToPage = pagePointToDevice.inverted(&invertible);
const QTransform deviceToPage = raster.pagePointToDevice.inverted(&invertible);
if (!invertible)
{
result.regions.clear();
Expand Down Expand Up @@ -310,4 +332,57 @@ PDFInkCoverageProbeResult PDFInkCoverageProbe::probe(const PDFPage* page,
return result;
}

PDFOverprintProbe::PDFOverprintProbe(PDFDocumentSession* session) :
m_session(session)
{
}

PDFOverprintProbeResult PDFOverprintProbe::probe(const PDFPage* page, int dpi, qint64 maxRasterPixels)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This duplicates PDFInkCoverageProbe::probe. About 50 lines are copied: box resolution, size and budget check, renderer settings, ink mapper and renderer construction. A shared "render page on the compositor" helper would keep the two probes from drifting apart. evaluateWhiteOverprintOnCompositor and evaluateTransparencyOverprintOnCompositor are also near-identical loops.

{
PDFOverprintProbeResult result;
if (!page || !m_session || dpi <= 0)
{
return result;
}

PDFDocument* document = m_session->getDocument();
if (!document)
{
return result;
}

const QRectF analysisBox = resolveAnalysisBox(page, PDFInkCoverageAnalysisBox::Bleed);
if (!isUsableBox(analysisBox))
{
return result;
}

const CompositorRaster raster = planCompositorRaster(page, analysisBox, dpi, maxRasterPixels);
result.budgetExceeded = raster.budgetExceeded;
if (raster.size.isEmpty())
{
return result;
}

PDFInkMapper inkMapper(nullptr, document);
inkMapper.createSpotColors(true);

PDFTransparencyRenderer renderer(page,
document,
m_session->getFontCache(),
m_session->getCMS(),
m_session->getOptionalContentActivity(),
&inkMapper,
compositorRendererSettings(),
raster.pagePointToDevice);
renderer.beginPaint(raster.size);
renderer.processContents();
renderer.endPaint();

result.rendered = true;
result.diagnostics = renderer.getRenderDiagnostics();
result.observation = renderer.getOverprintObservation();
return result;
}

} // namespace pdf
21 changes: 21 additions & 0 deletions LoopLibCore/sources/pdfinkcoverageprobe.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,27 @@ class LOOPLIBCORESHARED_EXPORT PDFInkCoverageProbe
PDFDocumentSession* m_session;
};

struct LOOPLIBCORESHARED_EXPORT PDFOverprintProbeResult
{
bool rendered = false; // false when rasterization was unavailable or over budget
bool budgetExceeded = false;
PDFRenderDiagnostics diagnostics;
PDFOverprintObservation observation;
};

/// Renders a page on the overprint-accurate compositor (the Output Preview path) and
/// reports the overprint that compositor applied, instead of reading page-view state.
class LOOPLIBCORESHARED_EXPORT PDFOverprintProbe
{
public:
explicit PDFOverprintProbe(PDFDocumentSession* session);

PDFOverprintProbeResult probe(const PDFPage* page, int dpi, qint64 maxRasterPixels);

private:
PDFDocumentSession* m_session;
};

} // namespace pdf

#endif // PDFINKCOVERAGEPROBE_H
Loading
Loading