diff --git a/Pdf4QtLib/sources/pdfdrawspacecontroller.h b/Pdf4QtLib/sources/pdfdrawspacecontroller.h index e1d2be6..192df6a 100644 --- a/Pdf4QtLib/sources/pdfdrawspacecontroller.h +++ b/Pdf4QtLib/sources/pdfdrawspacecontroller.h @@ -351,6 +351,7 @@ public: PDFWidget* getWidget() const { return m_widget; } bool isUsingOpenGL() const { return m_useOpenGL; } const QSurfaceFormat& getSurfaceFormat() const { return m_surfaceFormat; } + PageRotation getPageRotation() const { return m_controller->getPageRotation(); } void setFeatures(PDFRenderer::Features features); void setPreferredMeshResolutionRatio(PDFReal ratio); diff --git a/Pdf4QtLib/sources/pdfwidgettool.cpp b/Pdf4QtLib/sources/pdfwidgettool.cpp index 06871b5..80b12f4 100644 --- a/Pdf4QtLib/sources/pdfwidgettool.cpp +++ b/Pdf4QtLib/sources/pdfwidgettool.cpp @@ -19,6 +19,7 @@ #include "pdfdrawwidget.h" #include "pdfcompiler.h" #include "pdfwidgetutils.h" +#include "pdfpainterutils.h" #include "pdfdbgheap.h" #include @@ -745,6 +746,7 @@ PDFToolManager::PDFToolManager(PDFDrawWidgetProxy* proxy, Actions actions, QObje m_predefinedTools[PickRectangleTool] = pickTool; m_predefinedTools[FindTextTool] = new PDFFindTextTool(proxy, actions.findPrevAction, actions.findNextAction, this, parentDialog); m_predefinedTools[SelectTextTool] = new PDFSelectTextTool(proxy, actions.selectTextToolAction, actions.copyTextAction, actions.selectAllAction, actions.deselectAction, this); + m_predefinedTools[SelectTableTool] = new PDFSelectTableTool(proxy, actions.selectTableToolAction, this); m_predefinedTools[MagnifierTool] = new PDFMagnifierTool(proxy, actions.magnifierAction, this); m_predefinedTools[ScreenshotTool] = new PDFScreenshotTool(proxy, actions.screenshotToolAction, this); m_predefinedTools[ExtractImageTool] = new PDFExtractImageTool(proxy, actions.extractImageAction, this); @@ -1367,4 +1369,208 @@ void PDFExtractImageTool::onImagePicked(const QImage& image) } } +PDFSelectTableTool::PDFSelectTableTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent) : + BaseClass(proxy, action, parent), + m_pickTool(nullptr), + m_pageIndex(-1) +{ + m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this); + connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFSelectTableTool::onRectanglePicked); + + setCursor(Qt::CrossCursor); + updateActions(); +} + +void PDFSelectTableTool::drawPage(QPainter* painter, + PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QMatrix& pagePointToDevicePointMatrix, + QList& errors) const +{ + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + + if (isTablePicked() && pageIndex == m_pageIndex) + { + PDFPainterStateGuard guard(painter); + QColor color = QColor::fromRgbF(0.0, 0.0, 0.5, 0.2); + QRectF rectangle = pagePointToDevicePointMatrix.mapRect(m_pickedRectangle); + + const PDFReal lineWidth = PDFWidgetUtils::scaleDPI_x(getProxy()->getWidget(), 2.0); + QPen pen(Qt::SolidLine); + pen.setWidthF(lineWidth); + + painter->setPen(std::move(pen)); + painter->setBrush(QBrush(color)); + painter->drawRect(rectangle); + + for (const PDFReal columnPosition : m_horizontalBreaks) + { + QPointF startPoint(columnPosition, m_pickedRectangle.top()); + QPointF endPoint(columnPosition, m_pickedRectangle.bottom()); + + painter->drawLine(pagePointToDevicePointMatrix.map(startPoint), pagePointToDevicePointMatrix.map(endPoint)); + } + + for (const PDFReal rowPosition : m_verticalBreaks) + { + QPointF startPoint(m_pickedRectangle.left(), rowPosition); + QPointF endPoint(m_pickedRectangle.right(), rowPosition); + + painter->drawLine(pagePointToDevicePointMatrix.map(startPoint), pagePointToDevicePointMatrix.map(endPoint)); + } + } +} + +void PDFSelectTableTool::mousePressEvent(QWidget* widget, QMouseEvent* event) +{ + BaseClass::mousePressEvent(widget, event); + + if (event->isAccepted() || !isTablePicked()) + { + return; + } + + if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) + { + QPointF pagePoint; + const PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint); + if (pageIndex != -1 && pageIndex == m_pageIndex && m_pickedRectangle.contains(pagePoint)) + { + const PDFPage* page = getDocument()->getCatalog()->getPage(pageIndex); + bool isSelectingColumns = false; + + const PageRotation rotation = getPageRotationCombined(page->getPageRotation(), getProxy()->getPageRotation()); + switch (rotation) + { + case pdf::PageRotation::None: + case pdf::PageRotation::Rotate180: + isSelectingColumns = event->button() == Qt::LeftButton; + break; + + case pdf::PageRotation::Rotate90: + case pdf::PageRotation::Rotate270: + isSelectingColumns = event->button() == Qt::RightButton; + break; + + default: + Q_ASSERT(false); + break; + } + + const PDFReal distanceThresholdPixels = PDFWidgetUtils::scaleDPI_x(widget, 7.0); + const PDFReal distanceThreshold = getProxy()->transformPixelToDeviceSpace(distanceThresholdPixels); + + if (isSelectingColumns) + { + auto it = std::find_if(m_horizontalBreaks.begin(), m_horizontalBreaks.end(), [distanceThreshold, pagePoint](const PDFReal value) { return qAbs(value - pagePoint.x()) < distanceThreshold; }); + if (it != m_horizontalBreaks.end()) + { + m_horizontalBreaks.erase(it); + } + else if (pagePoint.x() > m_pickedRectangle.left() + distanceThreshold && pagePoint.x() < m_pickedRectangle.right() - distanceThreshold) + { + m_horizontalBreaks.insert(std::lower_bound(m_horizontalBreaks.begin(), m_horizontalBreaks.end(), pagePoint.x()), pagePoint.x()); + } + } + else + { + auto it = std::find_if(m_verticalBreaks.begin(), m_verticalBreaks.end(), [distanceThreshold, pagePoint](const PDFReal value) { return qAbs(value - pagePoint.y()) < distanceThreshold; }); + if (it != m_verticalBreaks.end()) + { + m_verticalBreaks.erase(it); + } + else if (pagePoint.y() > m_pickedRectangle.top() + distanceThreshold && pagePoint.y() < m_pickedRectangle.bottom() - distanceThreshold) + { + m_verticalBreaks.insert(std::lower_bound(m_verticalBreaks.begin(), m_verticalBreaks.end(), pagePoint.y()), pagePoint.y()); + } + } + + emit getProxy()->repaintNeeded(); + event->accept(); + } + } +} + +void PDFSelectTableTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event) +{ + BaseClass::mouseMoveEvent(widget, event); + + if (!event->isAccepted() && isTablePicked()) + { + QPointF pagePoint; + const PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint); + if (pageIndex != -1 && pageIndex == m_pageIndex && m_pickedRectangle.contains(pagePoint)) + { + setCursor(Qt::CrossCursor); + } + else + { + setCursor(Qt::ArrowCursor); + } + } +} + +void PDFSelectTableTool::setActiveImpl(bool active) +{ + BaseClass::setActiveImpl(active); + + if (active) + { + addTool(m_pickTool); + } + else + { + // Clear all data + setPageIndex(-1); + setPickedRectangle(QRectF()); + setTextLayout(PDFTextLayout()); + + m_horizontalBreaks.clear(); + m_verticalBreaks.clear(); + + if (getTopToolstackTool()) + { + removeTool(); + } + } +} + +void PDFSelectTableTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle) +{ + removeTool(); + + setPageIndex(pageIndex); + setPickedRectangle(pageRectangle); + setTextLayout(getProxy()->getTextLayoutCompiler()->createTextLayout(pageIndex)); + autodetectTableGeometry(); + + emit messageDisplayRequest(tr("Table region was selected. Use left/right mouse buttons to add/remove rows/columns, then use Enter key to copy the table."), 5000); +} + +void PDFSelectTableTool::autodetectTableGeometry() +{ + +} + +bool PDFSelectTableTool::isTablePicked() const +{ + return m_pageIndex != -1 && !m_pickedRectangle.isEmpty(); +} + +void PDFSelectTableTool::setTextLayout(PDFTextLayout&& newTextLayout) +{ + m_textLayout = std::move(newTextLayout); +} + +void PDFSelectTableTool::setPageIndex(PDFInteger newPageIndex) +{ + m_pageIndex = newPageIndex; +} + +void PDFSelectTableTool::setPickedRectangle(const QRectF& newPickedRectangle) +{ + m_pickedRectangle = newPickedRectangle; +} + } // namespace pdf diff --git a/Pdf4QtLib/sources/pdfwidgettool.h b/Pdf4QtLib/sources/pdfwidgettool.h index c85e78d..78be468 100644 --- a/Pdf4QtLib/sources/pdfwidgettool.h +++ b/Pdf4QtLib/sources/pdfwidgettool.h @@ -373,6 +373,53 @@ private: QColor m_selectionRectangleColor; }; +/// Tool for selection of table in document. Rows and columns +/// are automatically detected and are modifiable by the user. +class PDFSelectTableTool : public PDFWidgetTool +{ + Q_OBJECT + +private: + using BaseClass = PDFWidgetTool; + +public: + /// Construct new table selection tool + /// \param proxy Draw widget proxy + /// \param action Tool activation action + /// \param parent Parent object + explicit PDFSelectTableTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent); + + virtual void drawPage(QPainter* painter, + PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QMatrix& pagePointToDevicePointMatrix, + QList& errors) const override; + + virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override; + virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override; + +protected: + virtual void setActiveImpl(bool active) override; + +private: + void setPickedRectangle(const QRectF& newPickedRectangle); + void setPageIndex(PDFInteger newPageIndex); + void setTextLayout(PDFTextLayout&& newTextLayout); + void autodetectTableGeometry(); + + bool isTablePicked() const; + + void onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle); + + PDFPickTool* m_pickTool; + PDFInteger m_pageIndex; + QRectF m_pickedRectangle; + PDFTextLayout m_textLayout; + std::vector m_horizontalBreaks; + std::vector m_verticalBreaks; +}; + /// Tool that makes screenshot of page area and copies it to the clipboard, /// using current client area to determine image size. class PDF4QTLIBSHARED_EXPORT PDFScreenshotTool : public PDFWidgetTool @@ -436,6 +483,7 @@ public: QAction* findPrevAction = nullptr; ///< Action for navigating to previous result QAction* findNextAction = nullptr; ///< Action for navigating to next result QAction* selectTextToolAction = nullptr; + QAction* selectTableToolAction = nullptr; QAction* selectAllAction = nullptr; QAction* deselectAction = nullptr; QAction* copyTextAction = nullptr; @@ -460,6 +508,7 @@ public: PickRectangleTool, FindTextTool, SelectTextTool, + SelectTableTool, MagnifierTool, ScreenshotTool, ExtractImageTool, diff --git a/Pdf4QtViewer/pdf4qtviewer.qrc b/Pdf4QtViewer/pdf4qtviewer.qrc index 53a302b..1cd1bbc 100644 --- a/Pdf4QtViewer/pdf4qtviewer.qrc +++ b/Pdf4QtViewer/pdf4qtviewer.qrc @@ -91,5 +91,6 @@ resources/pce-same-size.svg resources/pce-same-width.svg resources/certificate-manager.svg + resources/select-table.svg diff --git a/Pdf4QtViewer/pdfaboutdialog.cpp b/Pdf4QtViewer/pdfaboutdialog.cpp index 0b2d72f..89bcdba 100644 --- a/Pdf4QtViewer/pdfaboutdialog.cpp +++ b/Pdf4QtViewer/pdfaboutdialog.cpp @@ -44,7 +44,7 @@ PDFAboutDialog::PDFAboutDialog(QWidget* parent) : ui->tableWidget->setSelectionMode(QTableView::SingleSelection); ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); - for (size_t i = 0; i < infos.size(); ++i) + for (int i = 0; i < int(infos.size()); ++i) { const pdf::PDFDependentLibraryInfo& info = infos[i]; ui->tableWidget->setItem(i, 0, new QTableWidgetItem(info.library)); diff --git a/Pdf4QtViewer/pdfprogramcontroller.cpp b/Pdf4QtViewer/pdfprogramcontroller.cpp index 85aeba1..81eee95 100644 --- a/Pdf4QtViewer/pdfprogramcontroller.cpp +++ b/Pdf4QtViewer/pdfprogramcontroller.cpp @@ -939,6 +939,7 @@ void PDFProgramController::initializeToolManager() actions.findPrevAction = m_actionManager->getAction(PDFActionManager::FindPrevious); actions.findNextAction = m_actionManager->getAction(PDFActionManager::FindNext); actions.selectTextToolAction = m_actionManager->getAction(PDFActionManager::ToolSelectText); + actions.selectTableToolAction = m_actionManager->getAction(PDFActionManager::ToolSelectTable); actions.selectAllAction = m_actionManager->getAction(PDFActionManager::SelectTextAll); actions.deselectAction = m_actionManager->getAction(PDFActionManager::DeselectText); actions.copyTextAction = m_actionManager->getAction(PDFActionManager::CopyText); diff --git a/Pdf4QtViewer/pdfprogramcontroller.h b/Pdf4QtViewer/pdfprogramcontroller.h index 33350be..25f2a2b 100644 --- a/Pdf4QtViewer/pdfprogramcontroller.h +++ b/Pdf4QtViewer/pdfprogramcontroller.h @@ -161,6 +161,7 @@ public: PageLayoutTwoColumns, PageLayoutFirstPageOnRightSide, ToolSelectText, + ToolSelectTable, ToolMagnifier, ToolScreenshot, ToolExtractImage, diff --git a/Pdf4QtViewer/pdfviewermainwindow.cpp b/Pdf4QtViewer/pdfviewermainwindow.cpp index af6039b..bebc3aa 100644 --- a/Pdf4QtViewer/pdfviewermainwindow.cpp +++ b/Pdf4QtViewer/pdfviewermainwindow.cpp @@ -181,6 +181,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : m_actionManager->setAction(PDFActionManager::PageLayoutTwoColumns, ui->actionPageLayoutTwoColumns); m_actionManager->setAction(PDFActionManager::PageLayoutFirstPageOnRightSide, ui->actionFirstPageOnRightSide); m_actionManager->setAction(PDFActionManager::ToolSelectText, ui->actionSelectText); + m_actionManager->setAction(PDFActionManager::ToolSelectTable, ui->actionSelectTable); m_actionManager->setAction(PDFActionManager::ToolMagnifier, ui->actionMagnifier); m_actionManager->setAction(PDFActionManager::ToolScreenshot, ui->actionScreenshot); m_actionManager->setAction(PDFActionManager::ToolExtractImage, ui->actionExtractImage); @@ -238,6 +239,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : // Tools ui->mainToolBar->addAction(ui->actionSelectText); + ui->mainToolBar->addAction(ui->actionSelectTable); ui->mainToolBar->addAction(ui->actionCreateTextHighlight); ui->mainToolBar->addAction(ui->actionCreateTextUnderline); ui->mainToolBar->addAction(ui->actionCreateTextStrikeout); diff --git a/Pdf4QtViewer/pdfviewermainwindow.ui b/Pdf4QtViewer/pdfviewermainwindow.ui index ef2c152..3d52bde 100644 --- a/Pdf4QtViewer/pdfviewermainwindow.ui +++ b/Pdf4QtViewer/pdfviewermainwindow.ui @@ -136,6 +136,8 @@ + + @@ -470,6 +472,18 @@ Select text + + + true + + + + :/resources/select-table.svg:/resources/select-table.svg + + + Select table + + diff --git a/Pdf4QtViewer/resources/select-table.svg b/Pdf4QtViewer/resources/select-table.svg new file mode 100644 index 0000000..3e47cb4 --- /dev/null +++ b/Pdf4QtViewer/resources/select-table.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + +