Issue #107: Enable/Disable image conversion GUI

This commit is contained in:
Jakub Melka 2023-11-17 11:00:46 +01:00
parent fc44dfa4bc
commit cf7d65dc82
4 changed files with 181 additions and 48 deletions

View File

@ -58,6 +58,7 @@ private:
class PDF4QTLIBSHARED_EXPORT PDFImage class PDF4QTLIBSHARED_EXPORT PDFImage
{ {
public: public:
PDFImage() = default;
/// Creates image from the content and the dictionary. If image can't be created, then exception is thrown. /// Creates image from the content and the dictionary. If image can't be created, then exception is thrown.
/// \param document Document /// \param document Document
@ -112,8 +113,6 @@ public:
const PDFImageData& getSoftMaskData() const { return m_softMask; } const PDFImageData& getSoftMaskData() const { return m_softMask; }
private: private:
PDFImage() = default;
PDFImageData m_imageData; PDFImageData m_imageData;
PDFImageData m_softMask; PDFImageData m_softMask;
PDFColorSpacePointer m_colorSpace; PDFColorSpacePointer m_colorSpace;

View File

@ -62,7 +62,7 @@ GENERATE_EXPORT_HEADER(Pdf4QtViewer
PDF4QTVIEWERLIBSHARED_EXPORT PDF4QTVIEWERLIBSHARED_EXPORT
EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}/pdf4qtviewer_export.h") EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}/pdf4qtviewer_export.h")
target_link_libraries(Pdf4QtViewer PRIVATE Pdf4QtLib Qt6::Core Qt6::Gui Qt6::Widgets Qt6::PrintSupport Qt6::TextToSpeech Qt6::Xml Qt6::OpenGLWidgets) target_link_libraries(Pdf4QtViewer PRIVATE Pdf4QtLib Qt6::Core Qt6::Gui Qt6::Widgets Qt6::PrintSupport Qt6::TextToSpeech Qt6::Xml Qt6::Svg Qt6::OpenGLWidgets)
target_include_directories(Pdf4QtViewer INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(Pdf4QtViewer INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(Pdf4QtViewer PUBLIC ${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}) target_include_directories(Pdf4QtViewer PUBLIC ${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR})

View File

@ -23,16 +23,122 @@
#include "pdfimage.h" #include "pdfimage.h"
#include "pdfdbgheap.h" #include "pdfdbgheap.h"
#include "pdfexception.h" #include "pdfexception.h"
#include "pdfwidgetutils.h"
#include <QCheckBox> #include <QCheckBox>
#include <QPushButton> #include <QPushButton>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
#include <QListWidget> #include <QListWidget>
#include <QStyledItemDelegate>
#include <QPainter>
#include <QtSvg/QSvgRenderer>
#include <QMouseEvent>
#include <QToolTip>
namespace pdfviewer namespace pdfviewer
{ {
class ImagePreviewDelegate : public QStyledItemDelegate
{
public:
ImagePreviewDelegate(std::vector<PDFCreateBitonalDocumentDialog::ImageConversionInfo>* imageConversionInfos, QObject* parent) :
QStyledItemDelegate(parent),
m_imageConversionInfos(imageConversionInfos)
{
m_yesRenderer.load(QString(":/resources/result-ok.svg"));
m_noRenderer.load(QString(":/resources/result-error.svg"));
}
virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
QStyledItemDelegate::paint(painter, option, index);
QRect markRect = getMarkRect(option);
if (index.isValid())
{
const PDFCreateBitonalDocumentDialog::ImageConversionInfo& info = m_imageConversionInfos->at(index.row());
if (info.conversionEnabled)
{
m_yesRenderer.render(painter, markRect);
}
else
{
m_noRenderer.render(painter, markRect);
}
}
}
virtual bool editorEvent(QEvent* event,
QAbstractItemModel* model,
const QStyleOptionViewItem& option,
const QModelIndex& index)
{
Q_UNUSED(model);
Q_UNUSED(index);
if (event->type() == QEvent::MouseButtonPress && index.isValid())
{
QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
if (mouseEvent && mouseEvent->button() == Qt::LeftButton)
{
// Do we click on yes/no mark?
QRectF markRect = getMarkRect(option);
if (markRect.contains(mouseEvent->position()))
{
PDFCreateBitonalDocumentDialog::ImageConversionInfo& info = m_imageConversionInfos->at(index.row());
info.conversionEnabled = !info.conversionEnabled;
return true;
}
}
}
return false;
}
virtual bool helpEvent(QHelpEvent* event,
QAbstractItemView* view,
const QStyleOptionViewItem& option,
const QModelIndex& index) override
{
Q_UNUSED(index);
if (!event || !view)
{
return false;
}
if (event->type() == QEvent::ToolTip)
{
// Are we hovering over yes/no mark?
QRectF markRect = getMarkRect(option);
if (markRect.contains(event->pos()))
{
event->accept();
QToolTip::showText(event->globalPos(), tr("Toggle this icon to switch image conversion to bitonic format on or off."), view);
return true;
}
}
return false;
}
private:
static constexpr QSize s_iconSize = QSize(24, 24);
QRect getMarkRect(const QStyleOptionViewItem& option) const
{
QSize markSize = pdf::PDFWidgetUtils::scaleDPI(option.widget, s_iconSize);
QRect markRect(option.rect.left(), option.rect.top(), markSize.width(), markSize.height());
return markRect;
}
std::vector<PDFCreateBitonalDocumentDialog::ImageConversionInfo>* m_imageConversionInfos;
mutable QSvgRenderer m_yesRenderer;
mutable QSvgRenderer m_noRenderer;
};
PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDocument* document, PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDocument* document,
const pdf::PDFCMS* cms, const pdf::PDFCMS* cms,
QWidget* parent) : QWidget* parent) :
@ -58,6 +164,8 @@ PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDoc
updateUi(); updateUi();
pdf::PDFWidgetUtils::style(this); pdf::PDFWidgetUtils::style(this);
ui->imageListWidget->setItemDelegate( new ImagePreviewDelegate(&m_imagesToBeConverted, this));
loadImages(); loadImages();
} }
@ -90,63 +198,39 @@ void PDFCreateBitonalDocumentDialog::loadImages()
ui->imageListWidget->setIconSize(iconSize); ui->imageListWidget->setIconSize(iconSize);
QSize imageSize = iconSize * ui->imageListWidget->devicePixelRatioF(); QSize imageSize = iconSize * ui->imageListWidget->devicePixelRatioF();
pdf::PDFCMSGeneric genericCms; int i = 0;
for (pdf::PDFObjectReference reference : m_imageReferences) for (pdf::PDFObjectReference reference : m_imageReferences)
{ {
std::optional<pdf::PDFImage> pdfImage; if (i++>10)
pdf::PDFObject imageObject = m_document->getObjectByReference(reference); {
break;
}
std::optional<pdf::PDFImage> pdfImage = getImageFromReference(reference);
if (!pdfImage)
{
continue;
}
pdf::PDFCMSGeneric genericCms;
pdf::PDFRenderErrorReporterDummy errorReporter; pdf::PDFRenderErrorReporterDummy errorReporter;
if (!imageObject.isStream())
{
// Image is not stream
continue;
}
const pdf::PDFStream* stream = imageObject.getStream();
try
{
pdf::PDFColorSpacePointer colorSpace;
const pdf::PDFDictionary* streamDictionary = stream->getDictionary();
if (streamDictionary->hasKey("ColorSpace"))
{
const pdf::PDFObject& colorSpaceObject = m_document->getObject(streamDictionary->get("ColorSpace"));
if (colorSpaceObject.isName() || colorSpaceObject.isArray())
{
pdf::PDFDictionary dummyDictionary;
colorSpace = pdf::PDFAbstractColorSpace::createColorSpace(&dummyDictionary, m_document, colorSpaceObject);
}
}
pdfImage.emplace(pdf::PDFImage::createImage(m_document,
stream,
colorSpace,
false,
pdf::RenderingIntent::Perceptual,
&errorReporter));
}
catch (pdf::PDFException)
{
continue;
}
QImage image = pdfImage->getImage(&genericCms, &errorReporter, nullptr); QImage image = pdfImage->getImage(&genericCms, &errorReporter, nullptr);
if (image.isNull()) if (image.isNull())
{ {
continue; continue;
} }
QListWidgetItem* item = new QListWidgetItem(ui->imageListWidget); QListWidgetItem* item = new QListWidgetItem(ui->imageListWidget);
QWidget* widget = new QWidget;
QHBoxLayout* layout = new QHBoxLayout(widget);
QCheckBox* checkbox = new QCheckBox(widget);
layout->addWidget(checkbox);
image = image.scaled(imageSize.width(), imageSize.height(), Qt::KeepAspectRatio, Qt::FastTransformation); image = image.scaled(imageSize.width(), imageSize.height(), Qt::KeepAspectRatio, Qt::FastTransformation);
item->setIcon(QIcon(QPixmap::fromImage(image))); item->setIcon(QIcon(QPixmap::fromImage(image)));
Qt::ItemFlags flags = item->flags();
flags.setFlag(Qt::ItemIsEditable, true);
item->setFlags(flags);
ui->imageListWidget->setItemWidget(item, widget); ImageConversionInfo imageConversionInfo;
imageConversionInfo.imageReference = reference;
imageConversionInfo.conversionEnabled = true;
m_imagesToBeConverted.push_back(imageConversionInfo);
} }
} }
@ -159,4 +243,45 @@ void PDFCreateBitonalDocumentDialog::updateUi()
m_createBitonalDocumentButton->setEnabled(!m_conversionInProgress); m_createBitonalDocumentButton->setEnabled(!m_conversionInProgress);
} }
std::optional<pdf::PDFImage> PDFCreateBitonalDocumentDialog::getImageFromReference(pdf::PDFObjectReference reference) const
{
std::optional<pdf::PDFImage> pdfImage;
pdf::PDFObject imageObject = m_document->getObjectByReference(reference);
pdf::PDFRenderErrorReporterDummy errorReporter;
if (!imageObject.isStream())
{
// Image is not stream
return pdfImage;
}
const pdf::PDFStream* stream = imageObject.getStream();
try
{
pdf::PDFColorSpacePointer colorSpace;
const pdf::PDFDictionary* streamDictionary = stream->getDictionary();
if (streamDictionary->hasKey("ColorSpace"))
{
const pdf::PDFObject& colorSpaceObject = m_document->getObject(streamDictionary->get("ColorSpace"));
if (colorSpaceObject.isName() || colorSpaceObject.isArray())
{
pdf::PDFDictionary dummyDictionary;
colorSpace = pdf::PDFAbstractColorSpace::createColorSpace(&dummyDictionary, m_document, colorSpaceObject);
}
}
pdfImage.emplace(pdf::PDFImage::createImage(m_document,
stream,
colorSpace,
false,
pdf::RenderingIntent::Perceptual,
&errorReporter));
}
catch (pdf::PDFException)
{
// Do nothing
}
return pdfImage;
}
} // namespace pdfviewer } // namespace pdfviewer

View File

@ -21,6 +21,7 @@
#include "pdfcms.h" #include "pdfcms.h"
#include "pdfdocument.h" #include "pdfdocument.h"
#include "pdfobjectutils.h" #include "pdfobjectutils.h"
#include "pdfimage.h"
#include <QDialog> #include <QDialog>
#include <QFuture> #include <QFuture>
@ -43,6 +44,12 @@ public:
pdf::PDFDocument takeBitonaldDocument() { return qMove(m_bitonalDocument); } pdf::PDFDocument takeBitonaldDocument() { return qMove(m_bitonalDocument); }
struct ImageConversionInfo
{
pdf::PDFObjectReference imageReference;
bool conversionEnabled = true;
};
private: private:
void createBitonalDocument(); void createBitonalDocument();
void onCreateBitonalDocumentButtonClicked(); void onCreateBitonalDocumentButtonClicked();
@ -50,6 +57,8 @@ private:
void updateUi(); void updateUi();
std::optional<pdf::PDFImage> getImageFromReference(pdf::PDFObjectReference reference) const;
Ui::PDFCreateBitonalDocumentDialog* ui; Ui::PDFCreateBitonalDocumentDialog* ui;
const pdf::PDFDocument* m_document; const pdf::PDFDocument* m_document;
const pdf::PDFCMS* m_cms; const pdf::PDFCMS* m_cms;
@ -60,7 +69,7 @@ private:
pdf::PDFDocument m_bitonalDocument; pdf::PDFDocument m_bitonalDocument;
pdf::PDFObjectClassifier m_classifier; pdf::PDFObjectClassifier m_classifier;
std::vector<pdf::PDFObjectReference> m_imageReferences; std::vector<pdf::PDFObjectReference> m_imageReferences;
std::vector<pdf::PDFObjectReference> m_imagesToBeConvertedReferences; std::vector<ImageConversionInfo> m_imagesToBeConverted;
}; };
} // namespace pdfviewer } // namespace pdfviewer