From e354a03564975c79cd726ac6d9ee115f908b1e87 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Sun, 5 Sep 2021 18:13:06 +0200 Subject: [PATCH] DocDiff application: basic functionality, opening documents --- Pdf4QtDocDiff/mainwindow.cpp | 211 ++++++++++++++++++++++++- Pdf4QtDocDiff/mainwindow.h | 25 +++ Pdf4QtDocDiff/mainwindow.ui | 64 +++++++- Pdf4QtDocDiff/resources.qrc | 5 + Pdf4QtDocDiff/resources/close.svg | 108 +++++++++++++ Pdf4QtDocDiff/resources/compare.svg | 108 +++++++++++++ Pdf4QtDocDiff/resources/get-source.svg | 108 +++++++++++++ Pdf4QtDocDiff/resources/open-left.svg | 108 +++++++++++++ Pdf4QtDocDiff/resources/open-right.svg | 108 +++++++++++++ Pdf4QtLib/sources/pdfdiff.cpp | 153 +++++++++++++++++- Pdf4QtLib/sources/pdfdiff.h | 93 ++++++++++- Pdf4QtLib/sources/pdfutils.cpp | 9 ++ Pdf4QtLib/sources/pdfutils.h | 4 + 13 files changed, 1088 insertions(+), 16 deletions(-) create mode 100644 Pdf4QtDocDiff/resources/close.svg create mode 100644 Pdf4QtDocDiff/resources/compare.svg create mode 100644 Pdf4QtDocDiff/resources/get-source.svg create mode 100644 Pdf4QtDocDiff/resources/open-left.svg create mode 100644 Pdf4QtDocDiff/resources/open-right.svg diff --git a/Pdf4QtDocDiff/mainwindow.cpp b/Pdf4QtDocDiff/mainwindow.cpp index 2545d8b..8f4b020 100644 --- a/Pdf4QtDocDiff/mainwindow.cpp +++ b/Pdf4QtDocDiff/mainwindow.cpp @@ -21,24 +21,47 @@ #include "aboutdialog.h" #include "pdfwidgetutils.h" +#include "pdfdocumentreader.h" #include #include #include +#include +#include +#include namespace pdfdocdiff { MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), - ui(new Ui::MainWindow) + ui(new Ui::MainWindow), + m_progress(new pdf::PDFProgress(this)), + m_taskbarButton(new QWinTaskbarButton(this)), + m_progressTaskbarIndicator(nullptr), + m_diff(nullptr), + m_isChangingProgressStep(false), + m_dontDisplayErrorMessage(false) { ui->setupUi(this); setMinimumSize(pdf::PDFWidgetUtils::scaleDPI(this, QSize(800, 600))); + // Initialize task bar progress + m_progressTaskbarIndicator = m_taskbarButton->progress(); + ui->actionGet_Source->setData(int(Operation::GetSource)); ui->actionAbout->setData(int(Operation::About)); + ui->actionOpen_Left->setData(int(Operation::OpenLeft)); + ui->actionOpen_Right->setData(int(Operation::OpenRight)); + ui->actionCompare->setData(int(Operation::Compare)); + ui->actionClose->setData(int(Operation::Close)); + + QToolBar* mainToolbar = addToolBar(tr("Main")); + mainToolbar->setObjectName("main_toolbar"); + mainToolbar->addActions({ ui->actionOpen_Left, ui->actionOpen_Right }); + mainToolbar->addSeparator(); + mainToolbar->addAction(ui->actionCompare); QSize iconSize = pdf::PDFWidgetUtils::scaleDPI(this, QSize(24, 24)); auto toolbars = findChildren(); @@ -61,6 +84,14 @@ MainWindow::MainWindow(QWidget* parent) : } } + connect(m_progress, &pdf::PDFProgress::progressStarted, this, &MainWindow::onProgressStarted); + connect(m_progress, &pdf::PDFProgress::progressStep, this, &MainWindow::onProgressStep); + connect(m_progress, &pdf::PDFProgress::progressFinished, this, &MainWindow::onProgressFinished); + + m_diff.setProgress(m_progress); + m_diff.setOption(pdf::PDFDiff::Asynchronous, true); + connect(&m_diff, &pdf::PDFDiff::comparationFinished, this, &MainWindow::onComparationFinished); + m_diff.setLeftDocument(&m_leftDocument); m_diff.setRightDocument(&m_rightDocument); @@ -74,11 +105,32 @@ MainWindow::~MainWindow() delete ui; } +void MainWindow::showEvent(QShowEvent* event) +{ + Q_UNUSED(event); + m_taskbarButton->setWindow(windowHandle()); +} + +void MainWindow::closeEvent(QCloseEvent* event) +{ + BaseClass::closeEvent(event); + m_diff.stop(); +} + void MainWindow::onMappedActionTriggered(int actionId) { performOperation(static_cast(actionId)); } +void MainWindow::onComparationFinished() +{ + auto result = m_diff.getResult().getResult(); + if (!result && !m_dontDisplayErrorMessage) + { + QMessageBox::critical(this, tr("Error"), result.getErrorMessage()); + } +} + void MainWindow::updateActions() { QList actions = findChildren(); @@ -138,6 +190,10 @@ bool MainWindow::canPerformOperation(Operation operation) const { switch (operation) { + case Operation::OpenLeft: + case Operation::OpenRight: + case Operation::Compare: + case Operation::Close: case Operation::GetSource: case Operation::About: return true; @@ -154,9 +210,103 @@ void MainWindow::performOperation(Operation operation) { switch (operation) { + case Operation::OpenLeft: + { + pdf::PDFTemporaryValueChange guard(&m_dontDisplayErrorMessage, true); + m_diff.stop(); + + std::optional document = openDocument(); + if (document) + { + m_leftDocument = std::move(*document); + + const size_t pageCount = m_leftDocument.getCatalog()->getPageCount(); + if (pageCount > 1) + { + ui->leftPageSelectionEdit->setText(QString("1-%2").arg(pageCount)); + } + else if (pageCount == 1) + { + ui->leftPageSelectionEdit->setText("1"); + } + else + { + ui->leftPageSelectionEdit->clear(); + } + } + break; + } + + case Operation::OpenRight: + { + pdf::PDFTemporaryValueChange guard(&m_dontDisplayErrorMessage, true); + m_diff.stop(); + + std::optional document = openDocument(); + if (document) + { + m_rightDocument = std::move(*document); + + const size_t pageCount = m_rightDocument.getCatalog()->getPageCount(); + if (pageCount > 1) + { + ui->rightPageSelectionEdit->setText(QString("1-%2").arg(pageCount)); + } + else if (pageCount == 1) + { + ui->rightPageSelectionEdit->setText("1"); + } + else + { + ui->rightPageSelectionEdit->clear(); + } + } + break; + } + + case Operation::Compare: + { + pdf::PDFTemporaryValueChange guard(&m_dontDisplayErrorMessage, true); + m_diff.stop(); + + QString errorMessage; + + pdf::PDFClosedIntervalSet rightPageIndices; + pdf::PDFClosedIntervalSet leftPageIndices = pdf::PDFClosedIntervalSet::parse(1, qMax(1, m_leftDocument.getCatalog()->getPageCount()), ui->leftPageSelectionEdit->text(), &errorMessage); + + if (errorMessage.isEmpty()) + { + rightPageIndices = pdf::PDFClosedIntervalSet::parse(1, qMax(1, m_rightDocument.getCatalog()->getPageCount()), ui->rightPageSelectionEdit->text(), &errorMessage); + } + + // Check if pages are succesfully parsed + if (!errorMessage.isEmpty()) + { + QMessageBox::critical(this, tr("Error"), errorMessage); + break; + } + + leftPageIndices.translate(-1); + rightPageIndices.translate(-1); + + m_diff.setPagesForLeftDocument(std::move(leftPageIndices)); + m_diff.setPagesForRightDocument(std::move(rightPageIndices)); + + m_diff.start(); + break; + } + + case Operation::Close: + { + close(); + break; + } + case Operation::GetSource: + { QDesktopServices::openUrl(QUrl("https://github.com/JakubMelka/PDF4QT")); break; + } case Operation::About: { @@ -174,4 +324,63 @@ void MainWindow::performOperation(Operation operation) updateActions(); } +std::optional MainWindow::openDocument() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Select PDF document"), m_settings.directory, tr("PDF document (*.pdf)")); + if (fileName.isEmpty()) + { + return std::nullopt; + } + + auto queryPassword = [this](bool* ok) + { + *ok = false; + return QInputDialog::getText(this, tr("Encrypted document"), tr("Enter password to access document content"), QLineEdit::Password, QString(), ok); + }; + + // Mark current directory as this + QFileInfo fileInfo(fileName); + m_settings.directory = fileInfo.dir().absolutePath(); + + // Try to open a new document + pdf::PDFDocumentReader reader(nullptr, qMove(queryPassword), true, false); + pdf::PDFDocument document = reader.readFromFile(fileName); + + QString errorMessage = reader.getErrorMessage(); + pdf::PDFDocumentReader::Result result = reader.getReadingResult(); + if (result == pdf::PDFDocumentReader::Result::OK) + { + return document; + } + else if (result == pdf::PDFDocumentReader::Result::Failed) + { + QMessageBox::critical(this, tr("Error"), errorMessage); + } + + return pdf::PDFDocument(); +} + +void MainWindow::onProgressStarted(pdf::ProgressStartupInfo info) +{ + m_progressTaskbarIndicator->setRange(0, 100); + m_progressTaskbarIndicator->reset(); + m_progressTaskbarIndicator->show(); +} + +void MainWindow::onProgressStep(int percentage) +{ + if (m_isChangingProgressStep) + { + return; + } + + pdf::PDFTemporaryValueChange guard(&m_isChangingProgressStep, true); + m_progressTaskbarIndicator->setValue(percentage); +} + +void MainWindow::onProgressFinished() +{ + m_progressTaskbarIndicator->hide(); +} + } // namespace pdfdocdiff diff --git a/Pdf4QtDocDiff/mainwindow.h b/Pdf4QtDocDiff/mainwindow.h index a7cacab..38dd6c4 100644 --- a/Pdf4QtDocDiff/mainwindow.h +++ b/Pdf4QtDocDiff/mainwindow.h @@ -23,6 +23,8 @@ #include #include +#include +#include namespace Ui { @@ -36,21 +38,36 @@ class MainWindow : public QMainWindow { Q_OBJECT +private: + using BaseClass = QMainWindow; + public: explicit MainWindow(QWidget* parent); virtual ~MainWindow() override; enum class Operation { + OpenLeft, + OpenRight, + Compare, + Close, GetSource, About }; + virtual void showEvent(QShowEvent* event) override; + virtual void closeEvent(QCloseEvent* event) override; + private slots: void updateActions(); private: void onMappedActionTriggered(int actionId); + void onComparationFinished(); + + void onProgressStarted(pdf::ProgressStartupInfo info); + void onProgressStep(int percentage); + void onProgressFinished(); void loadSettings(); void saveSettings(); @@ -58,6 +75,8 @@ private: bool canPerformOperation(Operation operation) const; void performOperation(Operation operation); + std::optional openDocument(); + struct Settings { QString directory; @@ -65,9 +84,15 @@ private: Ui::MainWindow* ui; + pdf::PDFProgress* m_progress; + QWinTaskbarButton* m_taskbarButton; + QWinTaskbarProgress* m_progressTaskbarIndicator; + Settings m_settings; QSignalMapper m_mapper; pdf::PDFDiff m_diff; + bool m_isChangingProgressStep; + bool m_dontDisplayErrorMessage; pdf::PDFDocument m_leftDocument; pdf::PDFDocument m_rightDocument; diff --git a/Pdf4QtDocDiff/mainwindow.ui b/Pdf4QtDocDiff/mainwindow.ui index 8adc04d..4d72ba8 100644 --- a/Pdf4QtDocDiff/mainwindow.ui +++ b/Pdf4QtDocDiff/mainwindow.ui @@ -81,6 +81,9 @@ File + + + @@ -94,15 +97,22 @@ Toolbars + + + Compare + + + + - - :/pdfdocpage/resources/get-source.svg:/pdfdocpage/resources/get-source.svg + + :/pdfdocdiff/resources/get-source.svg:/pdfdocdiff/resources/get-source.svg Get Source @@ -110,8 +120,8 @@ - - :/pdfdocpage/resources/about.svg:/pdfdocpage/resources/about.svg + + :/pdfdocdiff/resources/about.svg:/pdfdocdiff/resources/about.svg About @@ -120,7 +130,51 @@ F1 + + + + :/pdfdocdiff/resources/open-left.svg:/pdfdocdiff/resources/open-left.svg + + + Open Left + + + + + + :/pdfdocdiff/resources/open-right.svg:/pdfdocdiff/resources/open-right.svg + + + Open Right + + + + + + :/pdfdocdiff/resources/compare.svg:/pdfdocdiff/resources/compare.svg + + + Compare + + + F5 + + + + + + :/pdfdocdiff/resources/close.svg:/pdfdocdiff/resources/close.svg + + + Close + + + Ctrl+F4 + + - + + + diff --git a/Pdf4QtDocDiff/resources.qrc b/Pdf4QtDocDiff/resources.qrc index 08cdb6e..87b0a23 100644 --- a/Pdf4QtDocDiff/resources.qrc +++ b/Pdf4QtDocDiff/resources.qrc @@ -1,5 +1,10 @@ resources/about.svg + resources/close.svg + resources/compare.svg + resources/get-source.svg + resources/open-left.svg + resources/open-right.svg diff --git a/Pdf4QtDocDiff/resources/close.svg b/Pdf4QtDocDiff/resources/close.svg new file mode 100644 index 0000000..d7478b4 --- /dev/null +++ b/Pdf4QtDocDiff/resources/close.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + image/svg+xml + + + + + Jakub Melka + + + + + + + + + + + + + + + + + + + + diff --git a/Pdf4QtDocDiff/resources/compare.svg b/Pdf4QtDocDiff/resources/compare.svg new file mode 100644 index 0000000..d7478b4 --- /dev/null +++ b/Pdf4QtDocDiff/resources/compare.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + image/svg+xml + + + + + Jakub Melka + + + + + + + + + + + + + + + + + + + + diff --git a/Pdf4QtDocDiff/resources/get-source.svg b/Pdf4QtDocDiff/resources/get-source.svg new file mode 100644 index 0000000..d7478b4 --- /dev/null +++ b/Pdf4QtDocDiff/resources/get-source.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + image/svg+xml + + + + + Jakub Melka + + + + + + + + + + + + + + + + + + + + diff --git a/Pdf4QtDocDiff/resources/open-left.svg b/Pdf4QtDocDiff/resources/open-left.svg new file mode 100644 index 0000000..d7478b4 --- /dev/null +++ b/Pdf4QtDocDiff/resources/open-left.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + image/svg+xml + + + + + Jakub Melka + + + + + + + + + + + + + + + + + + + + diff --git a/Pdf4QtDocDiff/resources/open-right.svg b/Pdf4QtDocDiff/resources/open-right.svg new file mode 100644 index 0000000..d7478b4 --- /dev/null +++ b/Pdf4QtDocDiff/resources/open-right.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + image/svg+xml + + + + + Jakub Melka + + + + + + + + + + + + + + + + + + + + diff --git a/Pdf4QtLib/sources/pdfdiff.cpp b/Pdf4QtLib/sources/pdfdiff.cpp index 12aa177..7284204 100644 --- a/Pdf4QtLib/sources/pdfdiff.cpp +++ b/Pdf4QtLib/sources/pdfdiff.cpp @@ -16,23 +16,34 @@ // along with PDF4QT. If not, see . #include "pdfdiff.h" +#include "pdfdocumenttextflow.h" + +#include 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::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 diff --git a/Pdf4QtLib/sources/pdfdiff.h b/Pdf4QtLib/sources/pdfdiff.h index 97f5688..a5c177d 100644 --- a/Pdf4QtLib/sources/pdfdiff.h +++ b/Pdf4QtLib/sources/pdfdiff.h @@ -19,14 +19,32 @@ #define PDFDIFF_H #include "pdfdocument.h" +#include "pdfprogress.h" +#include "pdfutils.h" #include +#include +#include + +#include 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 m_future; + std::optional> m_futureWatcher; }; } // namespace pdf diff --git a/Pdf4QtLib/sources/pdfutils.cpp b/Pdf4QtLib/sources/pdfutils.cpp index 0432f2b..7725e8d 100644 --- a/Pdf4QtLib/sources/pdfutils.cpp +++ b/Pdf4QtLib/sources/pdfutils.cpp @@ -367,6 +367,15 @@ std::vector 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; diff --git a/Pdf4QtLib/sources/pdfutils.h b/Pdf4QtLib/sources/pdfutils.h index bd47cfe..04de974 100644 --- a/Pdf4QtLib/sources/pdfutils.h +++ b/Pdf4QtLib/sources/pdfutils.h @@ -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,