DocDiff application: basic functionality, opening documents

This commit is contained in:
Jakub Melka
2021-09-05 18:13:06 +02:00
parent ba13871a9c
commit e354a03564
13 changed files with 1088 additions and 16 deletions

View File

@@ -16,23 +16,34 @@
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfdiff.h"
#include "pdfdocumenttextflow.h"
#include <QtConcurrent/QtConcurrent>
namespace pdf
{
PDFDiff::PDFDiff(QObject* parent) :
BaseClass(parent),
m_progress(nullptr),
m_leftDocument(nullptr),
m_rightDocument(nullptr)
m_rightDocument(nullptr),
m_options(Asynchronous),
m_cancelled(false)
{
}
PDFDiff::~PDFDiff()
{
stop();
}
void PDFDiff::setLeftDocument(const PDFDocument* leftDocument)
{
if (m_leftDocument != leftDocument)
{
clear();
stop();
m_leftDocument = leftDocument;
}
}
@@ -41,15 +52,147 @@ void PDFDiff::setRightDocument(const PDFDocument* rightDocument)
{
if (m_rightDocument != rightDocument)
{
clear();
stop();
m_rightDocument = rightDocument;
}
}
void PDFDiff::clear()
void PDFDiff::setPagesForLeftDocument(PDFClosedIntervalSet pagesForLeftDocument)
{
stopEngine();
stop();
m_pagesForLeftDocument = std::move(pagesForLeftDocument);
}
void PDFDiff::setPagesForRightDocument(PDFClosedIntervalSet pagesForRightDocument)
{
stop();
m_pagesForRightDocument = std::move(pagesForRightDocument);
}
void PDFDiff::start()
{
// Jakub Melka: First, we must ensure, that comparation
// process is finished, otherwise we must wait for end.
// Then, create a new future watcher.
stop();
m_cancelled = false;
if (m_options.testFlag(Asynchronous))
{
m_futureWatcher = std::nullopt;
m_futureWatcher.emplace();
m_future = QtConcurrent::run(std::bind(&PDFDiff::perform, this));
connect(&*m_futureWatcher, &QFutureWatcher<PDFDiffResult>::finished, this, &PDFDiff::onComparationPerformed);
m_futureWatcher->setFuture(m_future);
}
else
{
// Just do comparation immediately
m_result = perform();
emit comparationFinished();
}
}
void PDFDiff::stop()
{
if (m_futureWatcher && !m_futureWatcher->isFinished())
{
// Do stop only if process doesn't finished already.
// If we are finished, we do not want to set cancelled state.
m_cancelled = true;
m_futureWatcher->waitForFinished();
}
}
PDFDiffResult PDFDiff::perform()
{
PDFDiffResult result;
if (!m_leftDocument || !m_rightDocument)
{
result.setResult(tr("No document to be compared."));
return result;
}
if (m_pagesForLeftDocument.isEmpty() || m_pagesForRightDocument.isEmpty())
{
result.setResult(tr("No page to be compared."));
return result;
}
auto leftPages = m_pagesForLeftDocument.unfold();
auto rightPages = m_pagesForRightDocument.unfold();
const size_t leftDocumentPageCount = m_leftDocument->getCatalog()->getPageCount();
const size_t rightDocumentPageCount = m_rightDocument->getCatalog()->getPageCount();
if (leftPages.front() < 0 ||
leftPages.back() >= PDFInteger(leftDocumentPageCount) ||
rightPages.front() < 0 ||
rightPages.back() >= PDFInteger(rightDocumentPageCount))
{
result.setResult(tr("Invalid page range."));
return result;
}
if (m_progress)
{
ProgressStartupInfo info;
info.showDialog = false;
info.text = tr("");
m_progress->start(StepLast, std::move(info));
}
// StepExtractContentLeftDocument
stepProgress();
// StepExtractContentRightDocument
stepProgress();
// StepExtractTextLeftDocument
pdf::PDFDocumentTextFlowFactory factoryLeftDocumentTextFlow;
factoryLeftDocumentTextFlow.setCalculateBoundingBoxes(true);
PDFDocumentTextFlow leftTextFlow = factoryLeftDocumentTextFlow.create(m_leftDocument, leftPages, PDFDocumentTextFlowFactory::Algorithm::Auto);
stepProgress();
// StepExtractTextRightDocument
pdf::PDFDocumentTextFlowFactory factoryRightDocumentTextFlow;
factoryRightDocumentTextFlow.setCalculateBoundingBoxes(true);
PDFDocumentTextFlow rightTextFlow = factoryRightDocumentTextFlow.create(m_rightDocument, rightPages, PDFDocumentTextFlowFactory::Algorithm::Auto);
stepProgress();
// StepCompare
stepProgress();
if (m_progress)
{
m_progress->finish();
}
return result;
}
void PDFDiff::stepProgress()
{
if (m_progress)
{
m_progress->step();
}
}
void PDFDiff::onComparationPerformed()
{
m_cancelled = false;
m_result = m_future.result();
emit comparationFinished();
}
PDFDiffResult::PDFDiffResult() :
m_result(true)
{
}
} // namespace pdf

View File

@@ -19,14 +19,32 @@
#define PDFDIFF_H
#include "pdfdocument.h"
#include "pdfprogress.h"
#include "pdfutils.h"
#include <QObject>
#include <QFuture>
#include <QFutureWatcher>
#include <atomic>
namespace pdf
{
class PDFDiffResult
{
public:
explicit PDFDiffResult();
void setResult(PDFOperationResult result) { m_result = std::move(result); }
const PDFOperationResult& getResult() const { return m_result; }
private:
PDFOperationResult m_result;
};
/// Diff engine for comparing two pdf documents.
class PDFDiff : public QObject
class PDF4QTLIBSHARED_EXPORT PDFDiff : public QObject
{
Q_OBJECT
@@ -35,19 +53,84 @@ private:
public:
explicit PDFDiff(QObject* parent);
virtual ~PDFDiff() override;
enum Option
{
None = 0x0000,
Asynchronous = 0x0001, ///< Compare document asynchronously
};
Q_DECLARE_FLAGS(Options, Option)
/// Source document (left)
/// \param leftDocument Document
void setLeftDocument(const PDFDocument* leftDocument);
/// Source document (right)(
/// \param rightDocument Document
void setRightDocument(const PDFDocument* rightDocument);
/// Clears data (but not source document pointers,
/// for them, use setters), also stops comparing engine.
void clear();
/// Source pages to be compared (left document)
/// \param pagesForLeftDocument Page indices
void setPagesForLeftDocument(PDFClosedIntervalSet pagesForLeftDocument);
/// Source pages to be compared (right document)
/// \param pagesForRightDocument Page indices
void setPagesForRightDocument(PDFClosedIntervalSet pagesForRightDocument);
/// Sets progress object
/// \param progress Progress object
void setProgress(PDFProgress* progress) { m_progress = progress; }
/// Enables or disables comparator engine option
/// \param option Option
/// \param enable Enable or disable option?
void setOption(Option option, bool enable) { m_options.setFlag(option, enable); }
/// Starts comparator engine. If asynchronous engine option
/// is enabled, then separate thread is started, in which two
/// document is compared, and then signal \p comparationFinished,
/// otherwise this function is blocking until comparation process
/// is finished.
void start();
/// Stops comparator engine. Result data are cleared.
void stop();
/// Returns result of a comparation process
const PDFDiffResult& getResult() const { return m_result; }
signals:
void comparationFinished();
private:
void stopEngine();
enum Steps
{
StepExtractContentLeftDocument,
StepExtractContentRightDocument,
StepExtractTextLeftDocument,
StepExtractTextRightDocument,
StepCompare,
StepLast
};
PDFDiffResult perform();
void stepProgress();
void onComparationPerformed();
PDFProgress* m_progress;
const PDFDocument* m_leftDocument;
const PDFDocument* m_rightDocument;
PDFClosedIntervalSet m_pagesForLeftDocument;
PDFClosedIntervalSet m_pagesForRightDocument;
Options m_options;
std::atomic_bool m_cancelled;
PDFDiffResult m_result;
QFuture<PDFDiffResult> m_future;
std::optional<QFutureWatcher<PDFDiffResult>> m_futureWatcher;
};
} // namespace pdf

View File

@@ -367,6 +367,15 @@ std::vector<PDFInteger> PDFClosedIntervalSet::unfold() const
return result;
}
void PDFClosedIntervalSet::translate(PDFInteger offset)
{
for (auto& interval : m_intervals)
{
interval.first += offset;
interval.second += offset;
}
}
PDFClosedIntervalSet PDFClosedIntervalSet::parse(PDFInteger first, PDFInteger last, const QString& text, QString* errorMessage)
{
PDFClosedIntervalSet result;

View File

@@ -694,6 +694,10 @@ public:
/// Returns true, if interval set is empty
bool isEmpty() const { return m_intervals.empty(); }
/// Translates interval set by a given offset
/// \param offset Offset
void translate(PDFInteger offset);
/// Parses text into closed interval set, text should be in form "1,3,4,7,-11,12-,52-53,-",
/// where 1,3,4,7 means single pages, -11 means range from \p first to 11, 12- means range
/// from 12 to \p last, and 52-53 means closed interval [52, 53]. If text is not in this form,