mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
#Issue 21: Table selection tool + graphic
This commit is contained in:
@@ -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);
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include "pdfdrawwidget.h"
|
||||
#include "pdfcompiler.h"
|
||||
#include "pdfwidgetutils.h"
|
||||
#include "pdfpainterutils.h"
|
||||
#include "pdfdbgheap.h"
|
||||
|
||||
#include <QLabel>
|
||||
@@ -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<PDFRenderError>& 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
|
||||
|
@@ -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<PDFRenderError>& 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<PDFReal> m_horizontalBreaks;
|
||||
std::vector<PDFReal> 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,
|
||||
|
Reference in New Issue
Block a user