Issue #107: Create document with converted bitonic images

This commit is contained in:
Jakub Melka 2023-11-17 15:25:25 +01:00
parent c5ddb521ed
commit 805c967b70
4 changed files with 156 additions and 8 deletions

View File

@ -25,6 +25,7 @@
#include "pdfexception.h"
#include "pdfwidgetutils.h"
#include "pdfimageconversion.h"
#include "pdfstreamfilters.h"
#include <QCheckBox>
#include <QPushButton>
@ -200,6 +201,7 @@ private:
PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDocument* document,
const pdf::PDFCMS* cms,
pdf::PDFProgress* progress,
QWidget* parent) :
QDialog(parent),
ui(new Ui::PDFCreateBitonalDocumentDialog),
@ -209,7 +211,8 @@ PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDoc
m_conversionInProgress(false),
m_processed(false),
m_leftPreviewWidget(new PDFCreateBitonalDocumentPreviewWidget(this)),
m_rightPreviewWidget(new PDFCreateBitonalDocumentPreviewWidget(this))
m_rightPreviewWidget(new PDFCreateBitonalDocumentPreviewWidget(this)),
m_progress(progress)
{
ui->setupUi(this);
@ -222,7 +225,7 @@ PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDoc
m_classifier.classify(document);
m_imageReferences = m_classifier.getObjectsByType(pdf::PDFObjectClassifier::Image);
m_createBitonalDocumentButton = ui->buttonBox->addButton(tr("Process"), QDialogButtonBox::ActionRole);
m_createBitonalDocumentButton = ui->buttonBox->addButton(tr("Perform"), QDialogButtonBox::ActionRole);
connect(m_createBitonalDocumentButton, &QPushButton::clicked, this, &PDFCreateBitonalDocumentDialog::onCreateBitonalDocumentButtonClicked);
connect(ui->automaticThresholdRadioButton, &QRadioButton::clicked, this, &PDFCreateBitonalDocumentDialog::updateUi);
connect(ui->manualThresholdRadioButton, &QRadioButton::clicked, this, &PDFCreateBitonalDocumentDialog::updateUi);
@ -231,7 +234,7 @@ PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDoc
connect(ui->imageListWidget, &QListWidget::currentItemChanged, this, &PDFCreateBitonalDocumentDialog::updatePreview);
connect(ui->thresholdEditBox, &QSpinBox::editingFinished, this, &PDFCreateBitonalDocumentDialog::updatePreview);
pdf::PDFWidgetUtils::scaleWidget(this, QSize(640, 380));
pdf::PDFWidgetUtils::scaleWidget(this, QSize(1024, 768));
updateUi();
pdf::PDFWidgetUtils::style(this);
@ -249,9 +252,93 @@ PDFCreateBitonalDocumentDialog::~PDFCreateBitonalDocumentDialog()
delete ui;
}
void PDFCreateBitonalDocumentDialog::onPerformFinished()
{
m_future.waitForFinished();
m_conversionInProgress = false;
m_processed = true;
updateUi();
}
void PDFCreateBitonalDocumentDialog::createBitonalDocument()
{
std::vector<ImageConversionInfo> imagesToBeConverted;
std::copy_if(m_imagesToBeConverted.begin(), m_imagesToBeConverted.end(), std::back_inserter(imagesToBeConverted), [](const auto& item) { return item.conversionEnabled; });
// Do we have something to be converted?
if (imagesToBeConverted.empty())
{
return;
}
pdf::ProgressStartupInfo info;
info.showDialog = true;
info.text = tr("Converting images...");
m_progress->start(imagesToBeConverted.size(), std::move(info));
pdf::PDFObjectStorage storage = m_document->getStorage();
pdf::PDFCMSGeneric genericCms;
pdf::PDFRenderErrorReporterDummy errorReporter;
for (int i = 0; i < imagesToBeConverted.size(); ++i)
{
pdf::PDFObjectReference reference = imagesToBeConverted[i].imageReference;
std::optional<pdf::PDFImage> pdfImage = getImageFromReference(reference);
QImage image;
try
{
image = pdfImage->getImage(&genericCms, &errorReporter, nullptr);
}
catch (pdf::PDFException)
{
// Do nothing
}
// Just for code safety - this should never occur in here.
if (image.isNull())
{
continue;
}
pdf::PDFImageConversion imageConversion;
imageConversion.setConversionMethod(m_conversionMethod);
imageConversion.setThreshold(m_manualThreshold);
imageConversion.setImage(image);
if (imageConversion.convert())
{
QImage bitonicImage = imageConversion.getConvertedImage();
Q_ASSERT(bitonicImage.format() == QImage::Format_Mono);
QByteArray imageData((const char*)bitonicImage.constBits(), bitonicImage.sizeInBytes());
QByteArray compressedData = pdf::PDFFlateDecodeFilter::compress(imageData);
pdf::PDFArray array;
array.appendItem(pdf::PDFObject::createName("FlateDecode"));
pdf::PDFDictionary dictionary;
dictionary.addEntry(pdf::PDFInplaceOrMemoryString("Type"), pdf::PDFObject::createName("XObject"));
dictionary.addEntry(pdf::PDFInplaceOrMemoryString("Subtype"), pdf::PDFObject::createName("Image"));
dictionary.addEntry(pdf::PDFInplaceOrMemoryString("Width"), pdf::PDFObject::createInteger(image.width()));
dictionary.addEntry(pdf::PDFInplaceOrMemoryString("Height"), pdf::PDFObject::createInteger(image.height()));
dictionary.addEntry(pdf::PDFInplaceOrMemoryString("ColorSpace"), pdf::PDFObject::createName("DeviceGray"));
dictionary.addEntry(pdf::PDFInplaceOrMemoryString("BitsPerComponent"), pdf::PDFObject::createInteger(1));
dictionary.addEntry(pdf::PDFInplaceOrMemoryString("Predictor"), pdf::PDFObject::createInteger(1));
dictionary.setEntry(pdf::PDFInplaceOrMemoryString("Length"), pdf::PDFObject::createInteger(compressedData.size()));
dictionary.setEntry(pdf::PDFInplaceOrMemoryString("Filters"), pdf::PDFObject::createArray(std::make_shared<pdf::PDFArray>(qMove(array))));
pdf::PDFObject imageObject = pdf::PDFObject::createStream(std::make_shared<pdf::PDFStream>(qMove(dictionary), qMove(compressedData)));
storage.setObject(reference, std::move(imageObject));
}
m_progress->step();
}
m_bitonalDocument = pdf::PDFDocument(std::move(storage), m_document->getInfo()->version, QByteArray());
m_progress->finish();
}
void PDFCreateBitonalDocumentDialog::onCreateBitonalDocumentButtonClicked()
@ -259,8 +346,14 @@ void PDFCreateBitonalDocumentDialog::onCreateBitonalDocumentButtonClicked()
Q_ASSERT(!m_conversionInProgress);
Q_ASSERT(!m_future.isRunning());
m_conversionMethod = ui->automaticThresholdRadioButton->isChecked() ? pdf::PDFImageConversion::ConversionMethod::Automatic : pdf::PDFImageConversion::ConversionMethod::Manual;
m_manualThreshold = ui->thresholdEditBox->value();
m_conversionInProgress = true;
m_future = QtConcurrent::run([this]() { createBitonalDocument(); });
m_futureWatcher.emplace();
connect(&m_futureWatcher.value(), &QFutureWatcher<void>::finished, this, &PDFCreateBitonalDocumentDialog::onPerformFinished);
m_futureWatcher->setFuture(m_future);
updateUi();
}
@ -273,7 +366,7 @@ void PDFCreateBitonalDocumentDialog::loadImages()
int i = 0;
for (pdf::PDFObjectReference reference : m_imageReferences)
{
if (i++>10)
if (i++>1)
{
break;
}
@ -286,7 +379,17 @@ void PDFCreateBitonalDocumentDialog::loadImages()
pdf::PDFCMSGeneric genericCms;
pdf::PDFRenderErrorReporterDummy errorReporter;
QImage image = pdfImage->getImage(&genericCms, &errorReporter, nullptr);
QImage image;
try
{
image = pdfImage->getImage(&genericCms, &errorReporter, nullptr);
}
catch (pdf::PDFException)
{
// Do nothing
}
if (image.isNull())
{
continue;

View File

@ -22,6 +22,8 @@
#include "pdfdocument.h"
#include "pdfobjectutils.h"
#include "pdfimage.h"
#include "pdfimageconversion.h"
#include "pdfprogress.h"
#include <QDialog>
#include <QFuture>
@ -57,7 +59,10 @@ class PDFCreateBitonalDocumentDialog : public QDialog
Q_OBJECT
public:
explicit PDFCreateBitonalDocumentDialog(const pdf::PDFDocument* document, const pdf::PDFCMS* cms, QWidget* parent);
explicit PDFCreateBitonalDocumentDialog(const pdf::PDFDocument* document,
const pdf::PDFCMS* cms,
pdf::PDFProgress* progress,
QWidget* parent);
virtual ~PDFCreateBitonalDocumentDialog() override;
pdf::PDFDocument takeBitonaldDocument() { return qMove(m_bitonalDocument); }
@ -71,6 +76,7 @@ public:
private:
void createBitonalDocument();
void onCreateBitonalDocumentButtonClicked();
void onPerformFinished();
void loadImages();
void updateUi();
@ -85,6 +91,7 @@ private:
bool m_conversionInProgress;
bool m_processed;
QFuture<void> m_future;
std::optional<QFutureWatcher<void>> m_futureWatcher;
pdf::PDFDocument m_bitonalDocument;
pdf::PDFObjectClassifier m_classifier;
std::vector<pdf::PDFObjectReference> m_imageReferences;
@ -95,6 +102,11 @@ private:
PDFCreateBitonalDocumentPreviewWidget* m_leftPreviewWidget;
PDFCreateBitonalDocumentPreviewWidget* m_rightPreviewWidget;
pdf::PDFProgress* m_progress;
pdf::PDFImageConversion::ConversionMethod m_conversionMethod = pdf::PDFImageConversion::ConversionMethod::Automatic;
int m_manualThreshold = 128;
};
} // namespace pdfviewer

View File

@ -73,5 +73,38 @@
</layout>
</widget>
<resources/>
<connections/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PDFCreateBitonalDocumentDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>370</x>
<y>508</y>
</hint>
<hint type="destinationlabel">
<x>370</x>
<y>264</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PDFCreateBitonalDocumentDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>370</x>
<y>508</y>
</hint>
<hint type="destinationlabel">
<x>370</x>
<y>264</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -1257,7 +1257,7 @@ void PDFProgramController::onActionSanitizeTriggered()
void PDFProgramController::onActionCreateBitonalDocumentTriggered()
{
auto cms = m_CMSManager->getCurrentCMS();
PDFCreateBitonalDocumentDialog dialog(m_pdfDocument.data(), cms.data(), m_mainWindow);
PDFCreateBitonalDocumentDialog dialog(m_pdfDocument.data(), cms.data(), m_progress, m_mainWindow);
if (dialog.exec() == QDialog::Accepted)
{