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 "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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue