mirror of
				https://github.com/JakubMelka/PDF4QT.git
				synced 2025-06-05 21:59:17 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			430 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			430 lines
		
	
	
		
			16 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 "outputpreviewdialog.h"
 | |
| #include "ui_outputpreviewdialog.h"
 | |
| 
 | |
| #include "pdfcms.h"
 | |
| #include "pdfrenderer.h"
 | |
| #include "pdfwidgetutils.h"
 | |
| #include "pdfdrawspacecontroller.h"
 | |
| 
 | |
| #include <QCloseEvent>
 | |
| #include <QColorDialog>
 | |
| #include <QtConcurrent/QtConcurrent>
 | |
| 
 | |
| namespace pdfplugin
 | |
| {
 | |
| 
 | |
| OutputPreviewDialog::OutputPreviewDialog(const pdf::PDFDocument* document, pdf::PDFWidget* widget, QWidget* parent) :
 | |
|     QDialog(parent, Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint),
 | |
|     ui(new Ui::OutputPreviewDialog),
 | |
|     m_inkMapper(widget->getCMSManager(), document),
 | |
|     m_inkMapperForRendering(widget->getCMSManager(), document),
 | |
|     m_document(document),
 | |
|     m_widget(widget),
 | |
|     m_needUpdateImage(false),
 | |
|     m_outputPreviewWidget(new OutputPreviewWidget(this)),
 | |
|     m_futureWatcher(nullptr)
 | |
| {
 | |
|     ui->setupUi(this);
 | |
| 
 | |
|     ui->pageIndexScrollBar->setMinimum(1);
 | |
|     ui->pageIndexScrollBar->setValue(1);
 | |
|     ui->pageIndexScrollBar->setMaximum(int(document->getCatalog()->getPageCount()));
 | |
| 
 | |
|     ui->frameViewLayout->insertWidget(0, m_outputPreviewWidget);
 | |
| 
 | |
|     ui->displayModeComboBox->addItem(tr("Separations"), OutputPreviewWidget::Separations);
 | |
|     ui->displayModeComboBox->addItem(tr("Color Warnings | Ink Coverage"), OutputPreviewWidget::ColorWarningInkCoverage);
 | |
|     ui->displayModeComboBox->addItem(tr("Color Warnings | Rich Black"), OutputPreviewWidget::ColorWarningRichBlack);
 | |
|     ui->displayModeComboBox->addItem(tr("Ink Coverage"), OutputPreviewWidget::InkCoverage);
 | |
|     ui->displayModeComboBox->addItem(tr("Shape Channel"), OutputPreviewWidget::ShapeChannel);
 | |
|     ui->displayModeComboBox->addItem(tr("Opacity Channel"), OutputPreviewWidget::OpacityChannel);
 | |
|     ui->displayModeComboBox->setCurrentIndex(0);
 | |
| 
 | |
|     m_outputPreviewWidget->setInkMapper(&m_inkMapper);
 | |
|     ui->inksTreeWidget->setMinimumHeight(pdf::PDFWidgetUtils::scaleDPI_y(ui->inksTreeWidget, 150));
 | |
| 
 | |
|     m_inkMapper.createSpotColors(ui->simulateSeparationsCheckBox->isChecked());
 | |
|     connect(ui->simulateSeparationsCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::onSimulateSeparationsChecked);
 | |
|     connect(ui->simulatePaperColorCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::onSimulatePaperColorChecked);
 | |
|     connect(ui->redPaperColorEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &OutputPreviewDialog::onPaperColorChanged);
 | |
|     connect(ui->greenPaperColorEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &OutputPreviewDialog::onPaperColorChanged);
 | |
|     connect(ui->bluePaperColorEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &OutputPreviewDialog::onPaperColorChanged);
 | |
|     connect(ui->pageIndexScrollBar, &QScrollBar::valueChanged, this, &OutputPreviewDialog::updatePageImage);
 | |
|     connect(ui->displayImagesCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::updatePageImage);
 | |
|     connect(ui->displayShadingCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::updatePageImage);
 | |
|     connect(ui->displayTextCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::updatePageImage);
 | |
|     connect(ui->displayTilingPatternsCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::updatePageImage);
 | |
|     connect(ui->displayVectorGraphicsCheckBox, &QCheckBox::clicked, this, &OutputPreviewDialog::updatePageImage);
 | |
|     connect(ui->inksTreeWidget->model(), &QAbstractItemModel::dataChanged, this, &OutputPreviewDialog::onInksChanged);
 | |
|     connect(ui->alarmColorButton, &QPushButton::clicked, this, &OutputPreviewDialog::onAlarmColorButtonClicked);
 | |
|     connect(ui->displayModeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &OutputPreviewDialog::onDisplayModeChanged);
 | |
|     connect(ui->inkCoverageLimitEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &OutputPreviewDialog::onInkCoverageLimitChanged);
 | |
|     connect(ui->richBlackLimitEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &OutputPreviewDialog::onRichBlackLimtiChanged);
 | |
| 
 | |
|     updatePageImage();
 | |
|     updateInks();
 | |
|     updatePaperColorWidgets();
 | |
|     updateAlarmColorButtonIcon();
 | |
|     onDisplayModeChanged();
 | |
|     onInkCoverageLimitChanged(ui->inkCoverageLimitEdit->value());
 | |
|     onRichBlackLimtiChanged(ui->richBlackLimitEdit->value());
 | |
|     pdf::PDFWidgetUtils::style(this);
 | |
| }
 | |
| 
 | |
| OutputPreviewDialog::~OutputPreviewDialog()
 | |
| {
 | |
|     delete ui;
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::resizeEvent(QResizeEvent* event)
 | |
| {
 | |
|     QDialog::resizeEvent(event);
 | |
|     updatePageImage();
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::closeEvent(QCloseEvent* event)
 | |
| {
 | |
|     if (!isRenderingDone())
 | |
|     {
 | |
|         event->ignore();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::showEvent(QShowEvent* event)
 | |
| {
 | |
|     Q_UNUSED(event);
 | |
| 
 | |
|     updatePageImage();
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::updateInks()
 | |
| {
 | |
|     ui->inksTreeWidget->setUpdatesEnabled(false);
 | |
|     ui->inksTreeWidget->clear();
 | |
| 
 | |
|     QTreeWidgetItem* processColorsRoot = new QTreeWidgetItem(ui->inksTreeWidget, QStringList(tr("Process Inks")));
 | |
|     QTreeWidgetItem* spotColorsRoot = new QTreeWidgetItem(ui->inksTreeWidget, QStringList(tr("Spot Inks")));
 | |
| 
 | |
|     processColorsRoot->setFlags(processColorsRoot->flags() | Qt::ItemIsUserCheckable);
 | |
|     processColorsRoot->setCheckState(0, Qt::Checked);
 | |
| 
 | |
|     spotColorsRoot->setFlags(spotColorsRoot->flags() | Qt::ItemIsUserCheckable);
 | |
|     spotColorsRoot->setCheckState(0, Qt::Checked);
 | |
| 
 | |
|     QSize iconSize = pdf::PDFWidgetUtils::scaleDPI(ui->inksTreeWidget, QSize(16, 16));
 | |
|     ui->inksTreeWidget->setIconSize(iconSize);
 | |
|     ui->inksTreeWidget->setRootIsDecorated(true);
 | |
| 
 | |
|     int colorIndex = 0;
 | |
|     std::vector<pdf::PDFInkMapper::ColorInfo> separations = m_inkMapper.getSeparations(4);
 | |
|     for (const auto& colorInfo : separations)
 | |
|     {
 | |
|         QTreeWidgetItem* item = nullptr;
 | |
|         if (!colorInfo.isSpot)
 | |
|         {
 | |
|             // Process color (ink)
 | |
|             item = new QTreeWidgetItem(processColorsRoot, QStringList(colorInfo.textName));
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             // Spot color (ink)
 | |
|             item = new QTreeWidgetItem(spotColorsRoot, QStringList(colorInfo.textName));
 | |
|         }
 | |
| 
 | |
|         if (colorInfo.color.isValid())
 | |
|         {
 | |
|             QPixmap icon(iconSize);
 | |
|             icon.fill(colorInfo.color);
 | |
|             item->setIcon(0, QIcon(icon));
 | |
|         }
 | |
| 
 | |
|         item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
 | |
|         item->setCheckState(0, Qt::Checked);
 | |
|         item->setData(0, Qt::UserRole, colorIndex++);
 | |
|     }
 | |
| 
 | |
|     if (processColorsRoot->childCount() == 0)
 | |
|     {
 | |
|         delete processColorsRoot;
 | |
|     }
 | |
| 
 | |
|     if (spotColorsRoot->childCount() == 0)
 | |
|     {
 | |
|         delete spotColorsRoot;
 | |
|     }
 | |
| 
 | |
|     ui->inksTreeWidget->expandAll();
 | |
|     ui->inksTreeWidget->setUpdatesEnabled(true);
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::updatePaperColorWidgets()
 | |
| {
 | |
|     const bool isPaperColorEnabled = ui->simulatePaperColorCheckBox->isChecked();
 | |
| 
 | |
|     ui->redPaperColorEdit->setEnabled(isPaperColorEnabled);
 | |
|     ui->greenPaperColorEdit->setEnabled(isPaperColorEnabled);
 | |
|     ui->bluePaperColorEdit->setEnabled(isPaperColorEnabled);
 | |
| 
 | |
|     if (!isPaperColorEnabled)
 | |
|     {
 | |
|         ui->redPaperColorEdit->setValue(1.0);
 | |
|         ui->greenPaperColorEdit->setValue(1.0);
 | |
|         ui->bluePaperColorEdit->setValue(1.0);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::updateAlarmColorButtonIcon()
 | |
| {
 | |
|     QSize iconSize = ui->alarmColorButton->iconSize();
 | |
|     QPixmap pixmap(iconSize);
 | |
|     pixmap.fill(m_outputPreviewWidget->getAlarmColor());
 | |
|     ui->alarmColorButton->setIcon(QIcon(pixmap));
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::onPaperColorChanged()
 | |
| {
 | |
|     const bool isPaperColorEnabled = ui->simulatePaperColorCheckBox->isChecked();
 | |
|     if (isPaperColorEnabled)
 | |
|     {
 | |
|         updatePageImage();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::onAlarmColorButtonClicked()
 | |
| {
 | |
|     QColorDialog colorDialog(m_outputPreviewWidget->getAlarmColor(), this);
 | |
|     if (colorDialog.exec() == QColorDialog::Accepted)
 | |
|     {
 | |
|         m_outputPreviewWidget->setAlarmColor(colorDialog.currentColor());
 | |
|         updateAlarmColorButtonIcon();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::onSimulateSeparationsChecked(bool checked)
 | |
| {
 | |
|     m_inkMapper.setSpotColorsActive(checked);
 | |
|     updateInks();
 | |
|     updatePageImage();
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::onSimulatePaperColorChecked(bool checked)
 | |
| {
 | |
|     Q_UNUSED(checked);
 | |
| 
 | |
|     updatePaperColorWidgets();
 | |
|     updatePageImage();
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::onDisplayModeChanged()
 | |
| {
 | |
|     m_outputPreviewWidget->setDisplayMode(OutputPreviewWidget::DisplayMode(ui->displayModeComboBox->currentData().toInt()));
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::onInksChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
 | |
| {
 | |
|     Q_UNUSED(topLeft);
 | |
|     Q_UNUSED(bottomRight);
 | |
| 
 | |
|     if (roles.contains(Qt::CheckStateRole))
 | |
|     {
 | |
|         updatePageImage();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::onInkCoverageLimitChanged(double value)
 | |
| {
 | |
|     m_outputPreviewWidget->setInkCoverageLimit(value / 100.0);
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::onRichBlackLimtiChanged(double value)
 | |
| {
 | |
|     m_outputPreviewWidget->setRichBlackLimit(value / 100.0);
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::updatePageImage()
 | |
| {
 | |
|     if (!isRenderingDone())
 | |
|     {
 | |
|         m_needUpdateImage = true;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     m_needUpdateImage = false;
 | |
| 
 | |
|     const pdf::PDFPage* page = m_document->getCatalog()->getPage(ui->pageIndexScrollBar->value() - 1);
 | |
|     if (!page)
 | |
|     {
 | |
|         m_outputPreviewWidget->clear();
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     QApplication::setOverrideCursor(Qt::WaitCursor);
 | |
| 
 | |
|     pdf::PDFRGB paperColor = pdf::PDFRGB{ 1.0f, 1.0f, 1.0f };
 | |
| 
 | |
|     // Active color mask
 | |
|     uint32_t activeColorMask = pdf::PDFPixelFormat::getAllColorsMask();
 | |
| 
 | |
|     const int itemCount = ui->inksTreeWidget->topLevelItemCount();
 | |
|     for (int i = 0; i < itemCount; ++i)
 | |
|     {
 | |
|         QTreeWidgetItem* rootItem = ui->inksTreeWidget->topLevelItem(i);
 | |
|         const bool isRootItemChecked = rootItem->data(0, Qt::CheckStateRole).toInt() == Qt::Checked;
 | |
| 
 | |
|         const int childCount = rootItem->childCount();
 | |
|         for (int j = 0; j < childCount; ++j)
 | |
|         {
 | |
|             QTreeWidgetItem* childItem = rootItem->child(j);
 | |
|             const bool isChecked = childItem->data(0, Qt::CheckStateRole).toInt() == Qt::Checked;
 | |
|             const bool isEnabled = isRootItemChecked && isChecked;
 | |
| 
 | |
|             if (!isEnabled)
 | |
|             {
 | |
|                 uint32_t colorIndex = childItem->data(0, Qt::UserRole).toInt();
 | |
|                 uint32_t colorFlags = 1 << colorIndex;
 | |
|                 activeColorMask = activeColorMask & ~colorFlags;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Paper color
 | |
|     if (ui->simulatePaperColorCheckBox)
 | |
|     {
 | |
|         paperColor[0] = ui->redPaperColorEdit->value();
 | |
|         paperColor[1] = ui->greenPaperColorEdit->value();
 | |
|         paperColor[2] = ui->bluePaperColorEdit->value();
 | |
|     }
 | |
| 
 | |
|     pdf::PDFTransparencyRendererSettings::Flags flags = pdf::PDFTransparencyRendererSettings::None;
 | |
|     flags.setFlag(pdf::PDFTransparencyRendererSettings::DisplayImages, ui->displayImagesCheckBox->isChecked());
 | |
|     flags.setFlag(pdf::PDFTransparencyRendererSettings::DisplayText, ui->displayTextCheckBox->isChecked());
 | |
|     flags.setFlag(pdf::PDFTransparencyRendererSettings::DisplayVectorGraphics, ui->displayVectorGraphicsCheckBox->isChecked());
 | |
|     flags.setFlag(pdf::PDFTransparencyRendererSettings::DisplayShadings, ui->displayShadingCheckBox->isChecked());
 | |
|     flags.setFlag(pdf::PDFTransparencyRendererSettings::DisplayTilingPatterns, ui->displayTilingPatternsCheckBox->isChecked());
 | |
|     flags.setFlag(pdf::PDFTransparencyRendererSettings::SaveOriginalProcessImage, true);
 | |
| 
 | |
|     m_inkMapperForRendering = m_inkMapper;
 | |
|     QSize renderSize = m_outputPreviewWidget->getPageImageSizeHint();
 | |
|     auto renderImage = [this, page, renderSize, paperColor, activeColorMask, flags]() -> RenderedImage
 | |
|     {
 | |
|         return renderPage(page, renderSize, paperColor, activeColorMask, flags);
 | |
|     };
 | |
| 
 | |
|     m_future = QtConcurrent::run(renderImage);
 | |
|     m_futureWatcher = new QFutureWatcher<RenderedImage>();
 | |
|     connect(m_futureWatcher, &QFutureWatcher<RenderedImage>::finished, this, &OutputPreviewDialog::onPageImageRendered);
 | |
|     m_futureWatcher->setFuture(m_future);
 | |
| }
 | |
| 
 | |
| OutputPreviewDialog::RenderedImage OutputPreviewDialog::renderPage(const pdf::PDFPage* page,
 | |
|                                                                    QSize renderSize,
 | |
|                                                                    pdf::PDFRGB paperColor,
 | |
|                                                                    uint32_t activeColorMask,
 | |
|                                                                    pdf::PDFTransparencyRendererSettings::Flags additionalFlags)
 | |
| {
 | |
|     RenderedImage result;
 | |
| 
 | |
|     QRectF pageRect = page->getRotatedMediaBox();
 | |
|     QSizeF pageSize = pageRect.size();
 | |
|     pageSize.scale(renderSize.width(), renderSize.height(), Qt::KeepAspectRatio);
 | |
|     QSize imageSize = pageSize.toSize();
 | |
| 
 | |
|     if (!imageSize.isValid())
 | |
|     {
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     pdf::PDFTransparencyRendererSettings settings;
 | |
|     settings.flags = additionalFlags;
 | |
| 
 | |
|     // 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, activeColorMask != pdf::PDFPixelFormat::getAllColorsMask());
 | |
|     settings.flags.setFlag(pdf::PDFTransparencyRendererSettings::SeparationSimulation, m_inkMapperForRendering.getActiveSpotColorCount() > 0);
 | |
|     settings.activeColorMask = activeColorMask;
 | |
| 
 | |
|     QTransform pagePointToDevicePoint = pdf::PDFRenderer::createPagePointToDevicePointMatrix(page, QRect(QPoint(0, 0), imageSize));
 | |
|     pdf::PDFDrawWidgetProxy* proxy = m_widget->getDrawWidgetProxy();
 | |
|     pdf::PDFCMSPointer cms = proxy->getCMSManager()->getCurrentCMS();
 | |
|     pdf::PDFTransparencyRenderer renderer(page, m_document, proxy->getFontCache(), cms.data(), proxy->getOptionalContentActivity(),
 | |
|                                           &m_inkMapperForRendering, settings, pagePointToDevicePoint);
 | |
| 
 | |
|     renderer.beginPaint(imageSize);
 | |
|     result.errors = renderer.processContents();
 | |
|     renderer.endPaint();
 | |
| 
 | |
|     QImage image = renderer.toImage(false, true, paperColor);
 | |
| 
 | |
|     result.image = qMove(image);
 | |
|     result.originalProcessImage = renderer.getOriginalProcessBitmap();
 | |
|     result.pageSize = page->getRotatedMediaBoxMM().size();
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::onPageImageRendered()
 | |
| {
 | |
|     QApplication::restoreOverrideCursor();
 | |
| 
 | |
|     if (m_future.isFinished())
 | |
|     {
 | |
|         RenderedImage result = m_future.result();
 | |
|         m_future = QFuture<RenderedImage>();
 | |
|         m_futureWatcher->deleteLater();
 | |
|         m_futureWatcher = nullptr;
 | |
| 
 | |
|         m_outputPreviewWidget->setPageImage(qMove(result.image), qMove(result.originalProcessImage), result.pageSize);
 | |
| 
 | |
|         if (m_needUpdateImage)
 | |
|         {
 | |
|             updatePageImage();
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool OutputPreviewDialog::isRenderingDone() const
 | |
| {
 | |
|     return !(m_futureWatcher && m_futureWatcher->isRunning());
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::accept()
 | |
| {
 | |
|     if (!isRenderingDone())
 | |
|     {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     QDialog::accept();
 | |
| }
 | |
| 
 | |
| void OutputPreviewDialog::reject()
 | |
| {
 | |
|     if (!isRenderingDone())
 | |
|     {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     QDialog::reject();
 | |
| }
 | |
| 
 | |
| } // namespace pdfplugin
 |