From cf7d65dc82e8ada86302a65401b20727468f0632 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Fri, 17 Nov 2023 11:00:46 +0100 Subject: [PATCH] Issue #107: Enable/Disable image conversion GUI --- Pdf4QtLib/sources/pdfimage.h | 3 +- Pdf4QtViewer/CMakeLists.txt | 2 +- .../pdfcreatebitonaldocumentdialog.cpp | 213 ++++++++++++++---- Pdf4QtViewer/pdfcreatebitonaldocumentdialog.h | 11 +- 4 files changed, 181 insertions(+), 48 deletions(-) diff --git a/Pdf4QtLib/sources/pdfimage.h b/Pdf4QtLib/sources/pdfimage.h index f92dbbc..b19e6e4 100644 --- a/Pdf4QtLib/sources/pdfimage.h +++ b/Pdf4QtLib/sources/pdfimage.h @@ -58,6 +58,7 @@ private: class PDF4QTLIBSHARED_EXPORT PDFImage { public: + PDFImage() = default; /// Creates image from the content and the dictionary. If image can't be created, then exception is thrown. /// \param document Document @@ -112,8 +113,6 @@ public: const PDFImageData& getSoftMaskData() const { return m_softMask; } private: - PDFImage() = default; - PDFImageData m_imageData; PDFImageData m_softMask; PDFColorSpacePointer m_colorSpace; diff --git a/Pdf4QtViewer/CMakeLists.txt b/Pdf4QtViewer/CMakeLists.txt index a32dad6..6ef4518 100644 --- a/Pdf4QtViewer/CMakeLists.txt +++ b/Pdf4QtViewer/CMakeLists.txt @@ -62,7 +62,7 @@ GENERATE_EXPORT_HEADER(Pdf4QtViewer PDF4QTVIEWERLIBSHARED_EXPORT 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 PUBLIC ${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}) diff --git a/Pdf4QtViewer/pdfcreatebitonaldocumentdialog.cpp b/Pdf4QtViewer/pdfcreatebitonaldocumentdialog.cpp index 1f16876..5f2f588 100644 --- a/Pdf4QtViewer/pdfcreatebitonaldocumentdialog.cpp +++ b/Pdf4QtViewer/pdfcreatebitonaldocumentdialog.cpp @@ -23,16 +23,122 @@ #include "pdfimage.h" #include "pdfdbgheap.h" #include "pdfexception.h" +#include "pdfwidgetutils.h" #include #include #include #include #include +#include +#include +#include +#include +#include namespace pdfviewer { +class ImagePreviewDelegate : public QStyledItemDelegate +{ +public: + ImagePreviewDelegate(std::vector* 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(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* m_imageConversionInfos; + mutable QSvgRenderer m_yesRenderer; + mutable QSvgRenderer m_noRenderer; +}; + PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDocument* document, const pdf::PDFCMS* cms, QWidget* parent) : @@ -58,6 +164,8 @@ PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDoc updateUi(); pdf::PDFWidgetUtils::style(this); + ui->imageListWidget->setItemDelegate( new ImagePreviewDelegate(&m_imagesToBeConverted, this)); + loadImages(); } @@ -90,63 +198,39 @@ void PDFCreateBitonalDocumentDialog::loadImages() ui->imageListWidget->setIconSize(iconSize); QSize imageSize = iconSize * ui->imageListWidget->devicePixelRatioF(); - pdf::PDFCMSGeneric genericCms; - + int i = 0; for (pdf::PDFObjectReference reference : m_imageReferences) { - std::optional pdfImage; - pdf::PDFObject imageObject = m_document->getObjectByReference(reference); + if (i++>10) + { + break; + } + + std::optional pdfImage = getImageFromReference(reference); + if (!pdfImage) + { + continue; + } + + pdf::PDFCMSGeneric genericCms; 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); - if (image.isNull()) { continue; } 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); 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); } +std::optional PDFCreateBitonalDocumentDialog::getImageFromReference(pdf::PDFObjectReference reference) const +{ + std::optional 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 diff --git a/Pdf4QtViewer/pdfcreatebitonaldocumentdialog.h b/Pdf4QtViewer/pdfcreatebitonaldocumentdialog.h index 1a3b37a..4cd2106 100644 --- a/Pdf4QtViewer/pdfcreatebitonaldocumentdialog.h +++ b/Pdf4QtViewer/pdfcreatebitonaldocumentdialog.h @@ -21,6 +21,7 @@ #include "pdfcms.h" #include "pdfdocument.h" #include "pdfobjectutils.h" +#include "pdfimage.h" #include #include @@ -43,6 +44,12 @@ public: pdf::PDFDocument takeBitonaldDocument() { return qMove(m_bitonalDocument); } + struct ImageConversionInfo + { + pdf::PDFObjectReference imageReference; + bool conversionEnabled = true; + }; + private: void createBitonalDocument(); void onCreateBitonalDocumentButtonClicked(); @@ -50,6 +57,8 @@ private: void updateUi(); + std::optional getImageFromReference(pdf::PDFObjectReference reference) const; + Ui::PDFCreateBitonalDocumentDialog* ui; const pdf::PDFDocument* m_document; const pdf::PDFCMS* m_cms; @@ -60,7 +69,7 @@ private: pdf::PDFDocument m_bitonalDocument; pdf::PDFObjectClassifier m_classifier; std::vector m_imageReferences; - std::vector m_imagesToBeConvertedReferences; + std::vector m_imagesToBeConverted; }; } // namespace pdfviewer