From dee253598392b7c4f33c2c38a746506d10657fb1 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Wed, 29 Sep 2021 16:59:13 +0200 Subject: [PATCH] DocDiff application: filters --- Pdf4QtDocDiff/mainwindow.cpp | 152 +++++++++++++++++++++++++-- Pdf4QtDocDiff/mainwindow.h | 23 +++- Pdf4QtDocDiff/mainwindow.ui | 142 +++++++++++++++++++++++++ Pdf4QtLib/sources/pdfdiff.cpp | 191 ++++++++++++++++++++++++++++++---- Pdf4QtLib/sources/pdfdiff.h | 110 ++++++++++++++++---- 5 files changed, 569 insertions(+), 49 deletions(-) diff --git a/Pdf4QtDocDiff/mainwindow.cpp b/Pdf4QtDocDiff/mainwindow.cpp index f4bb7d5..96cc91a 100644 --- a/Pdf4QtDocDiff/mainwindow.cpp +++ b/Pdf4QtDocDiff/mainwindow.cpp @@ -41,7 +41,8 @@ MainWindow::MainWindow(QWidget* parent) : m_progressTaskbarIndicator(nullptr), m_diff(nullptr), m_isChangingProgressStep(false), - m_dontDisplayErrorMessage(false) + m_dontDisplayErrorMessage(false), + m_diffNavigator(nullptr) { ui->setupUi(this); @@ -56,12 +57,40 @@ MainWindow::MainWindow(QWidget* parent) : ui->actionOpen_Right->setData(int(Operation::OpenRight)); ui->actionCompare->setData(int(Operation::Compare)); ui->actionClose->setData(int(Operation::Close)); + ui->actionPrevious_Difference->setData(int(Operation::PreviousDifference)); + ui->actionNext_Difference->setData(int(Operation::NextDifference)); + ui->actionCreate_Compare_Report->setData(int(Operation::CreateCompareReport)); + ui->actionFilter_Text->setData(int(Operation::FilterText)); + ui->actionFilter_Vector_Graphics->setData(int(Operation::FilterVectorGraphics)); + ui->actionFilter_Images->setData(int(Operation::FilterImages)); + ui->actionFilter_Shading->setData(int(Operation::FilterShading)); + ui->actionFilter_Page_Movement->setData(int(Operation::FilterPageMovement)); + ui->actionView_Differences->setData(int(Operation::ViewDifferences)); + ui->actionView_Left->setData(int(Operation::ViewLeft)); + ui->actionView_Right->setData(int(Operation::ViewRight)); + ui->actionView_Overlay->setData(int(Operation::ViewOverlay)); + ui->actionShow_Pages_with_Differences->setData(int(Operation::ShowPageswithDifferences)); + ui->actionSave_Differences_to_XML->setData(int(Operation::SaveDifferencesToXML)); QToolBar* mainToolbar = addToolBar(tr("Main")); mainToolbar->setObjectName("main_toolbar"); mainToolbar->addActions({ ui->actionOpen_Left, ui->actionOpen_Right }); mainToolbar->addSeparator(); mainToolbar->addAction(ui->actionCompare); + mainToolbar->addAction(ui->actionCreate_Compare_Report); + mainToolbar->addAction(ui->actionSave_Differences_to_XML); + + QToolBar* differencesToolbar = addToolBar(tr("Differences")); + differencesToolbar->setObjectName("differences_toolbar"); + differencesToolbar->addActions({ ui->actionPrevious_Difference, ui->actionNext_Difference }); + + QToolBar* viewToolbar = addToolBar(tr("View")); + viewToolbar->setObjectName("view_toolbar"); + viewToolbar->addActions({ ui->actionView_Differences, ui->actionView_Left, ui->actionView_Right, ui->actionView_Overlay }); + viewToolbar->addSeparator(); + viewToolbar->addAction(ui->actionShow_Pages_with_Differences); + viewToolbar->addSeparator(); + viewToolbar->addActions({ ui->actionFilter_Text, ui->actionFilter_Vector_Graphics, ui->actionFilter_Images, ui->actionFilter_Shading, ui->actionFilter_Page_Movement }); QSize iconSize = pdf::PDFWidgetUtils::scaleDPI(this, QSize(24, 24)); auto toolbars = findChildren(); @@ -95,8 +124,10 @@ MainWindow::MainWindow(QWidget* parent) : m_diff.setLeftDocument(&m_leftDocument); m_diff.setRightDocument(&m_rightDocument); + m_diffNavigator.setResult(&m_filteredDiffResult); + loadSettings(); - updateActions(); + updateAll(false); } MainWindow::~MainWindow() @@ -124,11 +155,21 @@ void MainWindow::onMappedActionTriggered(int actionId) void MainWindow::onComparationFinished() { - auto result = m_diff.getResult().getResult(); - if (!result && !m_dontDisplayErrorMessage) + m_diffResult = m_diff.getResult(); + + if (!m_dontDisplayErrorMessage) { - QMessageBox::critical(this, tr("Error"), result.getErrorMessage()); + if (!m_diffResult.getResult()) + { + QMessageBox::critical(this, tr("Error"), m_diffResult.getResult().getErrorMessage()); + } + if (m_diffResult.isSame()) + { + QMessageBox::information(this, tr("Info"), tr("No differences found between the compared documents."), QMessageBox::Ok); + } } + + updateAll(true); } void MainWindow::updateActions() @@ -139,7 +180,13 @@ void MainWindow::updateActions() QVariant actionData = action->data(); if (actionData.isValid()) { - action->setEnabled(canPerformOperation(static_cast(actionData.toInt()))); + bool canPerformAction = canPerformOperation(static_cast(actionData.toInt())); + action->setEnabled(canPerformAction); + + if (!canPerformAction && action->isCheckable()) + { + action->setChecked(false); + } } } } @@ -198,6 +245,38 @@ bool MainWindow::canPerformOperation(Operation operation) const case Operation::About: return true; + case Operation::ViewDifferences: + case Operation::ViewLeft: + case Operation::ViewRight: + case Operation::ViewOverlay: + return true; // Allow always to change a view + + case Operation::FilterText: + return m_diffResult.hasTextDifferences(); + + case Operation::FilterVectorGraphics: + return m_diffResult.hasVectorGraphicsDifferences(); + + case Operation::FilterImages: + return m_diffResult.hasImageDifferences(); + + case Operation::FilterShading: + return m_diffResult.hasShadingDifferences(); + + case Operation::FilterPageMovement: + return m_diffResult.hasPageMoveDifferences(); + + case Operation::PreviousDifference: + return m_diffNavigator.canGoPrevious(); + + case Operation::NextDifference: + return m_diffNavigator.canGoNext(); + + case Operation::CreateCompareReport: + case Operation::ShowPageswithDifferences: + case Operation::SaveDifferencesToXML: + return m_diffResult.isChanged(); + default: Q_ASSERT(false); break; @@ -234,6 +313,10 @@ void MainWindow::performOperation(Operation operation) ui->leftPageSelectionEdit->clear(); } } + else + { + ui->leftPageSelectionEdit->clear(); + } break; } @@ -261,6 +344,10 @@ void MainWindow::performOperation(Operation operation) ui->rightPageSelectionEdit->clear(); } } + else + { + ui->rightPageSelectionEdit->clear(); + } break; } @@ -314,6 +401,33 @@ void MainWindow::performOperation(Operation operation) aboutDialog.exec(); break; } + + case Operation::PreviousDifference: + m_diffNavigator.goPrevious(); + break; + + case Operation::NextDifference: + m_diffNavigator.goNext(); + break; + + case Operation::FilterText: + case Operation::FilterVectorGraphics: + case Operation::FilterImages: + case Operation::FilterShading: + case Operation::FilterPageMovement: + updateFilteredResult(); + break; + + case Operation::ViewDifferences: + case Operation::ViewLeft: + case Operation::ViewRight: + case Operation::ViewOverlay: + case Operation::ShowPageswithDifferences: + case Operation::SaveDifferencesToXML: + case Operation::CreateCompareReport: + Q_ASSERT(false); + break; + default: { Q_ASSERT(false); @@ -324,6 +438,32 @@ void MainWindow::performOperation(Operation operation) updateActions(); } +void MainWindow::updateAll(bool resetFilters) +{ + if (resetFilters) + { + ui->actionFilter_Page_Movement->setChecked(m_diffResult.hasPageMoveDifferences()); + ui->actionFilter_Text->setChecked(m_diffResult.hasTextDifferences()); + ui->actionFilter_Vector_Graphics->setChecked(m_diffResult.hasVectorGraphicsDifferences()); + ui->actionFilter_Images->setChecked(m_diffResult.hasImageDifferences()); + ui->actionFilter_Shading->setChecked(m_diffResult.hasShadingDifferences()); + } + + updateFilteredResult(); +} + +void MainWindow::updateFilteredResult() +{ + m_filteredDiffResult = m_diffResult.filter(ui->actionFilter_Page_Movement->isChecked(), + ui->actionFilter_Text->isChecked(), + ui->actionFilter_Vector_Graphics->isChecked(), + ui->actionFilter_Images->isChecked(), + ui->actionFilter_Shading->isChecked()); + m_diffNavigator.update(); + + updateActions(); +} + std::optional MainWindow::openDocument() { QString fileName = QFileDialog::getOpenFileName(this, tr("Select PDF document"), m_settings.directory, tr("PDF document (*.pdf)")); diff --git a/Pdf4QtDocDiff/mainwindow.h b/Pdf4QtDocDiff/mainwindow.h index 9feeeb7..6a275e8 100644 --- a/Pdf4QtDocDiff/mainwindow.h +++ b/Pdf4QtDocDiff/mainwindow.h @@ -52,7 +52,21 @@ public: Compare, Close, GetSource, - About + About, + PreviousDifference, + NextDifference, + CreateCompareReport, + FilterText, + FilterVectorGraphics, + FilterImages, + FilterShading, + FilterPageMovement, + ViewDifferences, + ViewLeft, + ViewRight, + ViewOverlay, + ShowPageswithDifferences, + SaveDifferencesToXML }; virtual void showEvent(QShowEvent* event) override; @@ -75,6 +89,9 @@ private: bool canPerformOperation(Operation operation) const; void performOperation(Operation operation); + void updateAll(bool resetFilters); + void updateFilteredResult(); + std::optional openDocument(); struct Settings @@ -96,6 +113,10 @@ private: pdf::PDFDocument m_leftDocument; pdf::PDFDocument m_rightDocument; + + pdf::PDFDiffResult m_diffResult; + pdf::PDFDiffResult m_filteredDiffResult; ///< Difference result with filters applied + pdf::PDFDiffResultNavigator m_diffNavigator; ///< Difference navigator }; } // namespace pdfdocdiff diff --git a/Pdf4QtDocDiff/mainwindow.ui b/Pdf4QtDocDiff/mainwindow.ui index 89744ec..f10ab54 100644 --- a/Pdf4QtDocDiff/mainwindow.ui +++ b/Pdf4QtDocDiff/mainwindow.ui @@ -102,8 +102,41 @@ Compare + + + + + + View + + + + View + + + + + + + + + Filter + + + + + + + + + + + + + + @@ -172,6 +205,115 @@ Ctrl+F4 + + + Previous Difference + + + Shift+F6 + + + + + Next Difference + + + F6 + + + + + Create Compare Report + + + + + true + + + Filter Text + + + + + true + + + Filter Vector Graphics + + + + + true + + + Filter Images + + + + + true + + + Filter Shading + + + + + true + + + Filter Page Movement + + + + + true + + + View Differences + + + + + true + + + View Left + + + + + true + + + View Right + + + + + true + + + View Overlay + + + + + true + + + Show Pages with Differences + + + Show Pages with Differences + + + + + Save Differences to XML + + diff --git a/Pdf4QtLib/sources/pdfdiff.cpp b/Pdf4QtLib/sources/pdfdiff.cpp index bf0160e..0a75c14 100644 --- a/Pdf4QtLib/sources/pdfdiff.cpp +++ b/Pdf4QtLib/sources/pdfdiff.cpp @@ -196,7 +196,7 @@ PDFDiffResult PDFDiff::perform() m_progress->start(StepLast, std::move(info)); } - performSteps(leftPages, rightPages); + performSteps(leftPages, rightPages, result); if (m_progress) { @@ -327,7 +327,9 @@ void PDFDiff::performPageMatching(const std::vector& leftPre PDFAlgorithmLongestCommonSubsequenceBase::markSequence(pageSequence, leftPagesMoved, rightPagesMoved); } -void PDFDiff::performSteps(const std::vector& leftPages, const std::vector& rightPages) +void PDFDiff::performSteps(const std::vector& leftPages, + const std::vector& rightPages, + PDFDiffResult& result) { std::vector leftPreparedPages; std::vector rightPreparedPages; @@ -449,7 +451,7 @@ void PDFDiff::performSteps(const std::vector& leftPages, const std:: // StepCompare if (!m_cancelled) { - performCompare(leftPreparedPages, rightPreparedPages, pageSequence, pageMatches); + performCompare(leftPreparedPages, rightPreparedPages, pageSequence, pageMatches, result); stepProgress(); } } @@ -457,7 +459,8 @@ void PDFDiff::performSteps(const std::vector& leftPages, const std:: void PDFDiff::performCompare(const std::vector& leftPreparedPages, const std::vector& rightPreparedPages, PDFAlgorithmLongestCommonSubsequenceBase::Sequence& pageSequence, - const std::map& pageMatches) + const std::map& pageMatches, + PDFDiffResult& result) { using AlgorithmLCS = PDFAlgorithmLongestCommonSubsequenceBase; @@ -471,11 +474,11 @@ void PDFDiff::performCompare(const std::vector& leftPrepared Q_ASSERT(pageMatches.contains(leftPreparedPages.at(item.index1).pageIndex)); const PDFInteger leftIndex = leftPreparedPages[item.index1].pageIndex; const PDFInteger rightIndex = pageMatches.at(leftIndex); - m_result.addPageMoved(leftIndex, rightIndex); + result.addPageMoved(leftIndex, rightIndex); } if (item.isMoved()) { - m_result.addPageMoved(leftPreparedPages[item.index1].pageIndex, rightPreparedPages[item.index2].pageIndex); + result.addPageMoved(leftPreparedPages[item.index1].pageIndex, rightPreparedPages[item.index2].pageIndex); } } @@ -527,20 +530,20 @@ void PDFDiff::performCompare(const std::vector& leftPrepared case PDFDiffHelper::GraphicPieceInfo::Type::Text: if (isTextComparedAsVectorGraphics) { - m_result.addRemovedTextCharContent(leftPageContext.pageIndex, info.boundingRect); + result.addRemovedTextCharContent(leftPageContext.pageIndex, info.boundingRect); } break; case PDFDiffHelper::GraphicPieceInfo::Type::VectorGraphics: - m_result.addRemovedVectorGraphicContent(leftPageContext.pageIndex, info.boundingRect); + result.addRemovedVectorGraphicContent(leftPageContext.pageIndex, info.boundingRect); break; case PDFDiffHelper::GraphicPieceInfo::Type::Image: - m_result.addRemovedImageContent(leftPageContext.pageIndex, info.boundingRect); + result.addRemovedImageContent(leftPageContext.pageIndex, info.boundingRect); break; case PDFDiffHelper::GraphicPieceInfo::Type::Shading: - m_result.addRemovedShadingContent(leftPageContext.pageIndex, info.boundingRect); + result.addRemovedShadingContent(leftPageContext.pageIndex, info.boundingRect); break; default: @@ -556,20 +559,20 @@ void PDFDiff::performCompare(const std::vector& leftPrepared case PDFDiffHelper::GraphicPieceInfo::Type::Text: if (isTextComparedAsVectorGraphics) { - m_result.addAddedTextCharContent(rightPageContext.pageIndex, info.boundingRect); + result.addAddedTextCharContent(rightPageContext.pageIndex, info.boundingRect); } break; case PDFDiffHelper::GraphicPieceInfo::Type::VectorGraphics: - m_result.addAddedVectorGraphicContent(rightPageContext.pageIndex, info.boundingRect); + result.addAddedVectorGraphicContent(rightPageContext.pageIndex, info.boundingRect); break; case PDFDiffHelper::GraphicPieceInfo::Type::Image: - m_result.addAddedImageContent(rightPageContext.pageIndex, info.boundingRect); + result.addAddedImageContent(rightPageContext.pageIndex, info.boundingRect); break; case PDFDiffHelper::GraphicPieceInfo::Type::Shading: - m_result.addAddedShadingContent(rightPageContext.pageIndex, info.boundingRect); + result.addAddedShadingContent(rightPageContext.pageIndex, info.boundingRect); break; default: @@ -588,7 +591,7 @@ void PDFDiff::performCompare(const std::vector& leftPrepared rightTextFlow.append(rightPageContext.text); } - m_result.addPageAdded(rightPageContext.pageIndex); + result.addPageAdded(rightPageContext.pageIndex); } if (item.isRemoved()) { @@ -599,7 +602,7 @@ void PDFDiff::performCompare(const std::vector& leftPrepared leftTextFlow.append(leftPageContext.text); } - m_result.addPageRemoved(leftPageContext.pageIndex); + result.addPageRemoved(leftPageContext.pageIndex); } } @@ -628,11 +631,11 @@ void PDFDiff::performCompare(const std::vector& leftPrepared if (item.isAdded()) { - m_result.addPageAdded(rightPreparedPages[item.index2].pageIndex); + result.addPageAdded(rightPreparedPages[item.index2].pageIndex); } if (item.isRemoved()) { - m_result.addPageRemoved(leftPreparedPages[item.index1].pageIndex); + result.addPageRemoved(leftPreparedPages[item.index1].pageIndex); } } } @@ -641,7 +644,7 @@ void PDFDiff::performCompare(const std::vector& leftPrepared QMutex mutex; // Jakub Melka: try to compare text differences - auto compareTexts = [this, &mutex](PDFDiffHelper::TextFlowDifferences& context) + auto compareTexts = [this, &mutex, &result](PDFDiffHelper::TextFlowDifferences& context) { using TextCompareItem = PDFDiffHelper::TextCompareItem; const bool isWordsComparingMode = m_options.testFlag(CompareWords); @@ -804,25 +807,27 @@ void PDFDiff::performCompare(const std::vector& leftPrepared QMutexLocker locker(&mutex); if (!leftString.isEmpty() && !rightString.isEmpty()) { - m_result.addTextReplaced(pageIndex1, pageIndex2, leftString, rightString, leftRectInfos, rightRectInfos); + result.addTextReplaced(pageIndex1, pageIndex2, leftString, rightString, leftRectInfos, rightRectInfos); } else { if (!leftString.isEmpty()) { - m_result.addTextRemoved(pageIndex1, leftString, leftRectInfos); + result.addTextRemoved(pageIndex1, leftString, leftRectInfos); } if (!rightString.isEmpty()) { - m_result.addTextAdded(pageIndex2, rightString, rightRectInfos); + result.addTextAdded(pageIndex2, rightString, rightRectInfos); } } } }; PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, textFlowDifferences.begin(), textFlowDifferences.end(), compareTexts); - //std::for_each(textFlowDifferences.begin(), textFlowDifferences.end(), compareTexts); + + // Jakub Melka: sort results + result.finalize(); } void PDFDiff::finalizeGraphicsPieces(PDFDiffPageContext& context) @@ -1053,6 +1058,22 @@ void PDFDiffResult::addTextReplaced(PDFInteger pageIndex1, m_differences.emplace_back(std::move(difference)); } +void PDFDiffResult::finalize() +{ + auto predicate = [](const Difference& l, const Difference& r) + { + return qMax(l.pageIndex1, l.pageIndex2) < qMax(r.pageIndex1, r.pageIndex2); + }; + + std::stable_sort(m_differences.begin(), m_differences.end(), predicate); + + m_typeFlags = 0; + for (const Difference& difference : m_differences) + { + m_typeFlags |= static_cast(difference.type); + } +} + QString PDFDiffResult::getMessage(size_t index) const { if (index >= m_differences.size()) @@ -1113,6 +1134,50 @@ QString PDFDiffResult::getMessage(size_t index) const return QString(); } +PDFDiffResult PDFDiffResult::filter(bool filterPageMoveDifferences, + bool filterTextDifferences, + bool filterVectorGraphicsDifferences, + bool filterImageDifferences, + bool filterShadingDifferences) +{ + PDFDiffResult filteredResult = *this; + + uint32_t typeFlags = 0; + + if (filterPageMoveDifferences) + { + typeFlags |= FLAGS_PAGE_MOVE; + } + + if (filterTextDifferences) + { + typeFlags |= FLAGS_TEXT; + } + + if (filterVectorGraphicsDifferences) + { + typeFlags |= FLAGS_VECTOR_GRAPHICS; + } + + if (filterImageDifferences) + { + typeFlags |= FLAGS_IMAGE; + } + + if (filterShadingDifferences) + { + typeFlags |= FLAGS_SHADING; + } + + auto remove = [typeFlags](const Difference& difference) + { + return (uint32_t(difference.type) & typeFlags) == 0; + }; + filteredResult.m_differences.erase(std::remove_if(filteredResult.m_differences.begin(), filteredResult.m_differences.end(), remove), filteredResult.m_differences.end()); + + return filteredResult; +} + void PDFDiffResult::addRectLeft(Difference& difference, QRectF rect) { difference.leftRectIndex = m_rects.size(); @@ -1376,4 +1441,84 @@ void PDFDiffHelper::refineTextRectangles(PDFDiffResult::RectInfos& items) items = std::move(refinedItems); } +PDFDiffResultNavigator::PDFDiffResultNavigator(QObject* parent) : + QObject(parent), + m_diffResult(nullptr), + m_currentIndex(0) +{ + +} + +PDFDiffResultNavigator::~PDFDiffResultNavigator() +{ + +} + +void PDFDiffResultNavigator::setResult(const PDFDiffResult* diffResult) +{ + if (m_diffResult != diffResult) + { + m_diffResult = diffResult; + emit selectionChanged(m_currentIndex); + } +} + +bool PDFDiffResultNavigator::isSelected() const +{ + const size_t limit = getLimit(); + return m_currentIndex >= 0 && m_currentIndex < limit; +} + +bool PDFDiffResultNavigator::canGoNext() const +{ + const size_t limit = getLimit(); + return limit > 0 && m_currentIndex + 1 < limit; +} + +bool PDFDiffResultNavigator::canGoPrevious() const +{ + const size_t limit = getLimit(); + return limit > 0 && m_currentIndex > 0; +} + +void PDFDiffResultNavigator::goNext() +{ + if (!canGoNext()) + { + return; + } + + ++m_currentIndex; + emit selectionChanged(m_currentIndex); +} + +void PDFDiffResultNavigator::goPrevious() +{ + if (!canGoPrevious()) + { + return; + } + + const size_t limit = getLimit(); + if (m_currentIndex >= limit) + { + m_currentIndex = limit - 1; + } + else + { + --m_currentIndex; + } + emit selectionChanged(m_currentIndex); +} + +void PDFDiffResultNavigator::update() +{ + const size_t limit = getLimit(); + if (limit > 0 && m_currentIndex >= limit) + { + m_currentIndex = limit - 1; + emit selectionChanged(m_currentIndex); + } +} + } // namespace pdf diff --git a/Pdf4QtLib/sources/pdfdiff.h b/Pdf4QtLib/sources/pdfdiff.h index 765f760..163cb8d 100644 --- a/Pdf4QtLib/sources/pdfdiff.h +++ b/Pdf4QtLib/sources/pdfdiff.h @@ -35,28 +35,28 @@ namespace pdf struct PDFDiffPageContext; -class PDFDiffResult +class PDF4QTLIBSHARED_EXPORT PDFDiffResult { public: explicit PDFDiffResult(); - enum class Type + enum class Type : uint32_t { - Invalid, - PageMoved, - PageAdded, - PageRemoved, - RemovedTextCharContent, - RemovedVectorGraphicContent, - RemovedImageContent, - RemovedShadingContent, - AddedTextCharContent, - AddedVectorGraphicContent, - AddedImageContent, - AddedShadingContent, - TextReplaced, - TextAdded, - TextRemoved, + Invalid = 0x0000, + PageMoved = 0x0001, + PageAdded = 0x0002, + PageRemoved = 0x0004, + RemovedTextCharContent = 0x0008, + RemovedVectorGraphicContent = 0x0010, + RemovedImageContent = 0x0020, + RemovedShadingContent = 0x0040, + AddedTextCharContent = 0x0080, + AddedVectorGraphicContent = 0x0100, + AddedImageContent = 0x0200, + AddedShadingContent = 0x0400, + TextReplaced = 0x0800, + TextAdded = 0x1000, + TextRemoved = 0x2000, }; using RectInfos = std::vector>; @@ -77,9 +77,33 @@ public: /// \param index Index QString getMessage(size_t index) const; + bool hasPageMoveDifferences() const { return m_typeFlags & FLAGS_PAGE_MOVE; } + bool hasTextDifferences() const { return m_typeFlags & FLAGS_TEXT; } + bool hasVectorGraphicsDifferences() const { return m_typeFlags & FLAGS_VECTOR_GRAPHICS; } + bool hasImageDifferences() const { return m_typeFlags & FLAGS_IMAGE; } + bool hasShadingDifferences() const { return m_typeFlags & FLAGS_SHADING; } + + /// Filters results using given critera + /// \param filterPageMoveDifferences Filter page move differences? + /// \param filterTextDifferences Filter text diffferences? + /// \param filterVectorGraphicsDifferences Filter vector graphics differences? + /// \param filterImageDifferences Filter image differences? + /// \param filterShadingDifferences Filter shading differences? + PDFDiffResult filter(bool filterPageMoveDifferences, + bool filterTextDifferences, + bool filterVectorGraphicsDifferences, + bool filterImageDifferences, + bool filterShadingDifferences); + private: friend class PDFDiff; + static constexpr uint32_t FLAGS_PAGE_MOVE = uint32_t(Type::PageMoved) | uint32_t(Type::PageAdded) | uint32_t(Type::PageRemoved); + static constexpr uint32_t FLAGS_TEXT = uint32_t(Type::RemovedTextCharContent) | uint32_t(Type::AddedTextCharContent) | uint32_t(Type::TextReplaced) | uint32_t(Type::TextAdded) | uint32_t(Type::TextRemoved); + static constexpr uint32_t FLAGS_VECTOR_GRAPHICS = uint32_t(Type::RemovedVectorGraphicContent) | uint32_t(Type::AddedVectorGraphicContent); + static constexpr uint32_t FLAGS_IMAGE = uint32_t(Type::RemovedImageContent) | uint32_t(Type::AddedImageContent); + static constexpr uint32_t FLAGS_SHADING = uint32_t(Type::RemovedShadingContent) | uint32_t(Type::AddedShadingContent); + void addPageMoved(PDFInteger pageIndex1, PDFInteger pageIndex2); void addPageAdded(PDFInteger pageIndex); void addPageRemoved(PDFInteger pageIndex); @@ -103,6 +127,8 @@ private: const RectInfos& rectInfos1, const RectInfos& rectInfos2); + void finalize(); + /// Single content difference descriptor. It describes type /// of difference (such as graphics, image, text change) on a page /// or on a list of multiple pages. @@ -131,6 +157,50 @@ private: RectInfos m_rects; ///< Rectangles with page indices PDFOperationResult m_result; QStringList m_strings; + uint32_t m_typeFlags = 0; +}; + +/// Class for result navigation, can go to next, or previous result. +class PDF4QTLIBSHARED_EXPORT PDFDiffResultNavigator : public QObject +{ + Q_OBJECT + +public: + explicit PDFDiffResultNavigator(QObject* parent); + virtual ~PDFDiffResultNavigator() override; + + void setResult(const PDFDiffResult* diffResult); + + /// Returns true, if valid result is selected + bool isSelected() const; + + /// Returns true if action go to next result can be performed, + /// otherwise false is returned. + bool canGoNext() const; + + /// Returns true if action go to previous result can be performed, + /// otherwise false is returned. + bool canGoPrevious() const; + + /// Goes to next result. If action cannot be performed, + /// nothing happens and signal is not emitted. + void goNext(); + + /// Goes to previous result. If action cannot be performed, + /// nothing happens and signal is not emitted. + void goPrevious(); + + /// Updates selection, if difference result was changed + void update(); + +signals: + void selectionChanged(size_t currentIndex); + +private: + size_t getLimit() const { return m_diffResult ? m_diffResult->getDifferencesCount() : 0; } + + const PDFDiffResult* m_diffResult; + size_t m_currentIndex; }; /// Diff engine for comparing two pdf documents. @@ -218,7 +288,8 @@ private: PDFDiffResult perform(); void stepProgress(); void performSteps(const std::vector& leftPages, - const std::vector& rightPages); + const std::vector& rightPages, + PDFDiffResult& result); void performPageMatching(const std::vector& leftPreparedPages, const std::vector& rightPreparedPages, PDFAlgorithmLongestCommonSubsequenceBase::Sequence& pageSequence, @@ -226,7 +297,8 @@ private: void performCompare(const std::vector& leftPreparedPages, const std::vector& rightPreparedPages, PDFAlgorithmLongestCommonSubsequenceBase::Sequence& pageSequence, - const std::map& pageMatches); + const std::map& pageMatches, + PDFDiffResult& result); void finalizeGraphicsPieces(PDFDiffPageContext& context); void onComparationPerformed();