mirror of https://github.com/JakubMelka/PDF4QT.git
Issue #107: Create document with converted bitonic images
This commit is contained in:
parent
c5ddb521ed
commit
805c967b70
|
@ -25,6 +25,7 @@
|
||||||
#include "pdfexception.h"
|
#include "pdfexception.h"
|
||||||
#include "pdfwidgetutils.h"
|
#include "pdfwidgetutils.h"
|
||||||
#include "pdfimageconversion.h"
|
#include "pdfimageconversion.h"
|
||||||
|
#include "pdfstreamfilters.h"
|
||||||
|
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
@ -200,6 +201,7 @@ private:
|
||||||
|
|
||||||
PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDocument* document,
|
PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDocument* document,
|
||||||
const pdf::PDFCMS* cms,
|
const pdf::PDFCMS* cms,
|
||||||
|
pdf::PDFProgress* progress,
|
||||||
QWidget* parent) :
|
QWidget* parent) :
|
||||||
QDialog(parent),
|
QDialog(parent),
|
||||||
ui(new Ui::PDFCreateBitonalDocumentDialog),
|
ui(new Ui::PDFCreateBitonalDocumentDialog),
|
||||||
|
@ -209,7 +211,8 @@ PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDoc
|
||||||
m_conversionInProgress(false),
|
m_conversionInProgress(false),
|
||||||
m_processed(false),
|
m_processed(false),
|
||||||
m_leftPreviewWidget(new PDFCreateBitonalDocumentPreviewWidget(this)),
|
m_leftPreviewWidget(new PDFCreateBitonalDocumentPreviewWidget(this)),
|
||||||
m_rightPreviewWidget(new PDFCreateBitonalDocumentPreviewWidget(this))
|
m_rightPreviewWidget(new PDFCreateBitonalDocumentPreviewWidget(this)),
|
||||||
|
m_progress(progress)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
@ -222,7 +225,7 @@ PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDoc
|
||||||
m_classifier.classify(document);
|
m_classifier.classify(document);
|
||||||
m_imageReferences = m_classifier.getObjectsByType(pdf::PDFObjectClassifier::Image);
|
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(m_createBitonalDocumentButton, &QPushButton::clicked, this, &PDFCreateBitonalDocumentDialog::onCreateBitonalDocumentButtonClicked);
|
||||||
connect(ui->automaticThresholdRadioButton, &QRadioButton::clicked, this, &PDFCreateBitonalDocumentDialog::updateUi);
|
connect(ui->automaticThresholdRadioButton, &QRadioButton::clicked, this, &PDFCreateBitonalDocumentDialog::updateUi);
|
||||||
connect(ui->manualThresholdRadioButton, &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->imageListWidget, &QListWidget::currentItemChanged, this, &PDFCreateBitonalDocumentDialog::updatePreview);
|
||||||
connect(ui->thresholdEditBox, &QSpinBox::editingFinished, 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();
|
updateUi();
|
||||||
pdf::PDFWidgetUtils::style(this);
|
pdf::PDFWidgetUtils::style(this);
|
||||||
|
|
||||||
|
@ -249,9 +252,93 @@ PDFCreateBitonalDocumentDialog::~PDFCreateBitonalDocumentDialog()
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDFCreateBitonalDocumentDialog::onPerformFinished()
|
||||||
|
{
|
||||||
|
m_future.waitForFinished();
|
||||||
|
m_conversionInProgress = false;
|
||||||
|
m_processed = true;
|
||||||
|
updateUi();
|
||||||
|
}
|
||||||
|
|
||||||
void PDFCreateBitonalDocumentDialog::createBitonalDocument()
|
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()
|
void PDFCreateBitonalDocumentDialog::onCreateBitonalDocumentButtonClicked()
|
||||||
|
@ -259,8 +346,14 @@ void PDFCreateBitonalDocumentDialog::onCreateBitonalDocumentButtonClicked()
|
||||||
Q_ASSERT(!m_conversionInProgress);
|
Q_ASSERT(!m_conversionInProgress);
|
||||||
Q_ASSERT(!m_future.isRunning());
|
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_conversionInProgress = true;
|
||||||
m_future = QtConcurrent::run([this]() { createBitonalDocument(); });
|
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();
|
updateUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +366,7 @@ void PDFCreateBitonalDocumentDialog::loadImages()
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (pdf::PDFObjectReference reference : m_imageReferences)
|
for (pdf::PDFObjectReference reference : m_imageReferences)
|
||||||
{
|
{
|
||||||
if (i++>10)
|
if (i++>1)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -286,7 +379,17 @@ void PDFCreateBitonalDocumentDialog::loadImages()
|
||||||
|
|
||||||
pdf::PDFCMSGeneric genericCms;
|
pdf::PDFCMSGeneric genericCms;
|
||||||
pdf::PDFRenderErrorReporterDummy errorReporter;
|
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())
|
if (image.isNull())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include "pdfdocument.h"
|
#include "pdfdocument.h"
|
||||||
#include "pdfobjectutils.h"
|
#include "pdfobjectutils.h"
|
||||||
#include "pdfimage.h"
|
#include "pdfimage.h"
|
||||||
|
#include "pdfimageconversion.h"
|
||||||
|
#include "pdfprogress.h"
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
|
@ -57,7 +59,10 @@ class PDFCreateBitonalDocumentDialog : public QDialog
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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;
|
virtual ~PDFCreateBitonalDocumentDialog() override;
|
||||||
|
|
||||||
pdf::PDFDocument takeBitonaldDocument() { return qMove(m_bitonalDocument); }
|
pdf::PDFDocument takeBitonaldDocument() { return qMove(m_bitonalDocument); }
|
||||||
|
@ -71,6 +76,7 @@ public:
|
||||||
private:
|
private:
|
||||||
void createBitonalDocument();
|
void createBitonalDocument();
|
||||||
void onCreateBitonalDocumentButtonClicked();
|
void onCreateBitonalDocumentButtonClicked();
|
||||||
|
void onPerformFinished();
|
||||||
void loadImages();
|
void loadImages();
|
||||||
|
|
||||||
void updateUi();
|
void updateUi();
|
||||||
|
@ -85,6 +91,7 @@ private:
|
||||||
bool m_conversionInProgress;
|
bool m_conversionInProgress;
|
||||||
bool m_processed;
|
bool m_processed;
|
||||||
QFuture<void> m_future;
|
QFuture<void> m_future;
|
||||||
|
std::optional<QFutureWatcher<void>> m_futureWatcher;
|
||||||
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;
|
||||||
|
@ -95,6 +102,11 @@ private:
|
||||||
|
|
||||||
PDFCreateBitonalDocumentPreviewWidget* m_leftPreviewWidget;
|
PDFCreateBitonalDocumentPreviewWidget* m_leftPreviewWidget;
|
||||||
PDFCreateBitonalDocumentPreviewWidget* m_rightPreviewWidget;
|
PDFCreateBitonalDocumentPreviewWidget* m_rightPreviewWidget;
|
||||||
|
|
||||||
|
pdf::PDFProgress* m_progress;
|
||||||
|
|
||||||
|
pdf::PDFImageConversion::ConversionMethod m_conversionMethod = pdf::PDFImageConversion::ConversionMethod::Automatic;
|
||||||
|
int m_manualThreshold = 128;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdfviewer
|
} // namespace pdfviewer
|
||||||
|
|
|
@ -73,5 +73,38 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<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>
|
</ui>
|
||||||
|
|
|
@ -1257,7 +1257,7 @@ void PDFProgramController::onActionSanitizeTriggered()
|
||||||
void PDFProgramController::onActionCreateBitonalDocumentTriggered()
|
void PDFProgramController::onActionCreateBitonalDocumentTriggered()
|
||||||
{
|
{
|
||||||
auto cms = m_CMSManager->getCurrentCMS();
|
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)
|
if (dialog.exec() == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue