mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-01-14 17:45:59 +01:00
Advanced snapping, basics of screenshot tool
This commit is contained in:
parent
46c267e537
commit
13bfbd7095
@ -1370,4 +1370,15 @@ void IDocumentDrawInterface::drawPostRendering(QPainter* painter, QRect rect) co
|
||||
Q_UNUSED(rect);
|
||||
}
|
||||
|
||||
const PDFWidgetSnapshot::SnapshotItem* PDFWidgetSnapshot::getPageSnapshot(PDFInteger pageIndex) const
|
||||
{
|
||||
auto it = std::find_if(items.cbegin(), items.cend(), [pageIndex](const auto& item) { return item.pageIndex == pageIndex; });
|
||||
if (it != items.cend())
|
||||
{
|
||||
return &*it;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -196,6 +196,9 @@ struct PDFWidgetSnapshot
|
||||
const PDFPrecompiledPage* compiledPage = nullptr; ///< Compiled page (can be nullptr)
|
||||
};
|
||||
|
||||
bool hasPage(PDFInteger pageIndex) const { return getPageSnapshot(pageIndex) != nullptr; }
|
||||
const SnapshotItem* getPageSnapshot(PDFInteger pageIndex) const;
|
||||
|
||||
using SnapshotItems = std::vector<SnapshotItem>;
|
||||
SnapshotItems items;
|
||||
};
|
||||
|
@ -179,6 +179,41 @@ void PDFSnapper::buildSnapPoints(const PDFWidgetSnapshot& snapshot)
|
||||
viewportSnapPoint.viewportPoint = item.pageToDeviceMatrix.map(snapPoint.point);
|
||||
m_snapPoints.push_back(qMove(viewportSnapPoint));
|
||||
}
|
||||
|
||||
// Fill line projections snap points
|
||||
if (m_currentPage == item.pageIndex && m_referencePoint.has_value())
|
||||
{
|
||||
QPointF referencePoint = *m_referencePoint;
|
||||
for (const QLineF& line : info->getLines())
|
||||
{
|
||||
// Project point onto line.
|
||||
const qreal lineLength = line.length();
|
||||
QPointF vector = referencePoint - line.p1();
|
||||
QPointF tangentVector = (line.p2() - line.p1()) / lineLength;
|
||||
const qreal absoluteParameter = QPointF::dotProduct(vector, tangentVector);
|
||||
if (absoluteParameter >= 0 && absoluteParameter <= lineLength)
|
||||
{
|
||||
QPointF projectedSnapPoint = line.pointAt(absoluteParameter / lineLength);
|
||||
const PDFReal tolerance = lineLength * 0.01;
|
||||
const PDFReal squaredTolerance = tolerance * tolerance;
|
||||
|
||||
// Test, if projected snap point is not already present in snap points
|
||||
auto testSamePoints = [projectedSnapPoint, squaredTolerance](const ViewportSnapPoint& testedSnapPoint)
|
||||
{
|
||||
return isFuzzyComparedPointsSame(projectedSnapPoint, testedSnapPoint.point, squaredTolerance);
|
||||
};
|
||||
if (m_snapPoints.cend() == std::find_if(m_snapPoints.cbegin(), m_snapPoints.cend(), testSamePoints))
|
||||
{
|
||||
ViewportSnapPoint viewportSnapPoint;
|
||||
viewportSnapPoint.type = SnapType::GeneratedLineProjection;
|
||||
viewportSnapPoint.point = projectedSnapPoint;
|
||||
viewportSnapPoint.pageIndex = item.pageIndex;
|
||||
viewportSnapPoint.viewportPoint = item.pageToDeviceMatrix.map(projectedSnapPoint);
|
||||
m_snapPoints.push_back(qMove(viewportSnapPoint));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Third, update snap shot position
|
||||
@ -205,4 +240,16 @@ QPointF PDFSnapper::getSnappedPoint() const
|
||||
return m_mousePoint;
|
||||
}
|
||||
|
||||
void PDFSnapper::setReferencePoint(PDFInteger pageIndex, QPointF pagePoint)
|
||||
{
|
||||
m_currentPage = pageIndex;
|
||||
m_referencePoint = pagePoint;
|
||||
}
|
||||
|
||||
void PDFSnapper::clearReferencePoint()
|
||||
{
|
||||
m_currentPage = -1;
|
||||
m_referencePoint = std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -81,6 +81,9 @@ public:
|
||||
/// Returns snap points
|
||||
const std::vector<SnapPoint>& getSnapPoints() const { return m_snapPoints; }
|
||||
|
||||
/// Returns lines
|
||||
const std::vector<QLineF>& getLines() const { return m_snapLines; }
|
||||
|
||||
private:
|
||||
std::vector<SnapPoint> m_snapPoints;
|
||||
std::vector<QLineF> m_snapLines;
|
||||
@ -135,6 +138,14 @@ public:
|
||||
/// mouse cursor position is returned.
|
||||
QPointF getSnappedPoint() const;
|
||||
|
||||
/// Sets current page index and reference point
|
||||
/// \param pageIndex Page index
|
||||
/// \param pagePoint Page point
|
||||
void setReferencePoint(PDFInteger pageIndex, QPointF pagePoint);
|
||||
|
||||
/// Resets reference point (and current page)
|
||||
void clearReferencePoint();
|
||||
|
||||
private:
|
||||
|
||||
struct ViewportSnapPoint : public PDFSnapInfo::SnapPoint
|
||||
@ -152,6 +163,7 @@ private:
|
||||
std::vector<ViewportSnapPoint> m_snapPoints;
|
||||
std::optional<ViewportSnapPoint> m_snappedPoint;
|
||||
QPointF m_mousePoint;
|
||||
std::optional<QPointF> m_referencePoint;
|
||||
PDFInteger m_currentPage = -1;
|
||||
int m_snapPointPixelSize = 0;
|
||||
int m_snapPointTolerance = 0;
|
||||
|
@ -471,6 +471,18 @@ private:
|
||||
T m_q;
|
||||
};
|
||||
|
||||
/// Fuzzy compares two points, with given tolerance (so, if points are at lower distance
|
||||
/// from each other than squared tolerance, they are considered as same and function returns true).
|
||||
/// \param p1 First point
|
||||
/// \param p2 Second point
|
||||
/// \param squaredTolerance Squared tolerance
|
||||
static inline bool isFuzzyComparedPointsSame(const QPointF& p1, const QPointF& p2, PDFReal squaredTolerance)
|
||||
{
|
||||
QPointF dp = p2 - p1;
|
||||
const qreal squaredDistance = QPointF::dotProduct(dp, dp);
|
||||
return squaredDistance < squaredTolerance;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFUTILS_H
|
||||
|
@ -52,7 +52,7 @@ PDFWidgetTool::PDFWidgetTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject
|
||||
m_action(action),
|
||||
m_proxy(proxy)
|
||||
{
|
||||
|
||||
updateActions();
|
||||
}
|
||||
|
||||
PDFWidgetTool::~PDFWidgetTool()
|
||||
@ -949,7 +949,8 @@ void PDFMagnifierTool::setMagnifierSize(int magnifierSize)
|
||||
|
||||
PDFPickTool::PDFPickTool(PDFDrawWidgetProxy* proxy, PDFPickTool::Mode mode, QObject* parent) :
|
||||
BaseClass(proxy, parent),
|
||||
m_mode(mode)
|
||||
m_mode(mode),
|
||||
m_pageIndex(-1)
|
||||
{
|
||||
setCursor(Qt::BlankCursor);
|
||||
m_snapper.setSnapPointPixelSize(PDFWidgetUtils::scaleDPI_x(proxy->getWidget(), 10));
|
||||
@ -959,6 +960,36 @@ PDFPickTool::PDFPickTool(PDFDrawWidgetProxy* proxy, PDFPickTool::Mode mode, QObj
|
||||
connect(proxy, &PDFDrawWidgetProxy::pageImageChanged, this, &PDFPickTool::buildSnapPoints);
|
||||
}
|
||||
|
||||
void PDFPickTool::drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix) const
|
||||
{
|
||||
Q_UNUSED(compiledPage);
|
||||
Q_UNUSED(layoutGetter);
|
||||
|
||||
// If we are picking rectangles, then draw current selection rectangle
|
||||
if (m_mode == Mode::Rectangles && m_pageIndex == pageIndex && !m_pickedPoints.empty())
|
||||
{
|
||||
QPoint p1 = pagePointToDevicePointMatrix.map(m_pickedPoints.back()).toPoint();
|
||||
QPoint p2 = m_snapper.getSnappedPoint().toPoint();
|
||||
|
||||
int xMin = qMin(p1.x(), p2.x());
|
||||
int xMax = qMax(p1.x(), p2.x());
|
||||
int yMin = qMin(p1.y(), p2.y());
|
||||
int yMax = qMax(p1.y(), p2.y());
|
||||
|
||||
QRect selectionRectangle(xMin, yMin, xMax - xMin, yMax - yMin);
|
||||
if (selectionRectangle.isValid())
|
||||
{
|
||||
QColor selectionColor(Qt::blue);
|
||||
selectionColor.setAlphaF(0.25);
|
||||
painter->fillRect(selectionRectangle, selectionColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPickTool::drawPostRendering(QPainter* painter, QRect rect) const
|
||||
{
|
||||
m_snapper.drawSnapPoints(painter);
|
||||
@ -983,6 +1014,48 @@ void PDFPickTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
event->accept();
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
// Try to perform pick
|
||||
QPointF pagePoint;
|
||||
PDFInteger pageIndex = getProxy()->getPageUnderPoint(m_snapper.getSnappedPoint().toPoint(), &pagePoint);
|
||||
if (pageIndex != -1 && // We have picked some point on page
|
||||
(m_pageIndex == -1 || m_pageIndex == pageIndex)) // We are under current page
|
||||
{
|
||||
m_pageIndex = pageIndex;
|
||||
m_pickedPoints.push_back(pagePoint);
|
||||
m_snapper.setReferencePoint(pageIndex, pagePoint);
|
||||
|
||||
// Emit signal about picked point
|
||||
emit pointPicked(pageIndex, pagePoint);
|
||||
|
||||
if (m_mode == Mode::Rectangles && m_pickedPoints.size() == 2)
|
||||
{
|
||||
QPointF first = m_pickedPoints.front();
|
||||
QPointF second = m_pickedPoints.back();
|
||||
|
||||
const qreal xMin = qMin(first.x(), second.x());
|
||||
const qreal xMax = qMax(first.x(), second.x());
|
||||
const qreal yMin = qMin(first.y(), second.y());
|
||||
const qreal yMax = qMax(first.y(), second.y());
|
||||
|
||||
QRectF pageRectangle(xMin, yMin, xMax - xMin, yMax - yMin);
|
||||
emit rectanglePicked(pageIndex, pageRectangle);
|
||||
|
||||
// We must reset tool, to pick next rectangle
|
||||
resetTool();
|
||||
}
|
||||
|
||||
buildSnapPoints();
|
||||
getProxy()->repaintNeeded();
|
||||
}
|
||||
}
|
||||
else if (event->button() == Qt::RightButton)
|
||||
{
|
||||
// Reset tool to enable new picking (right button means reset the tool)
|
||||
resetTool();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPickTool::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
|
||||
@ -1012,6 +1085,22 @@ void PDFPickTool::setActiveImpl(bool active)
|
||||
{
|
||||
buildSnapPoints();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset tool to reinitialize it for future use. If tool
|
||||
// is activated, then it should be in initial state.
|
||||
resetTool();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPickTool::resetTool()
|
||||
{
|
||||
m_pickedPoints.clear();
|
||||
m_pageIndex = -1;
|
||||
m_snapper.clearReferencePoint();
|
||||
|
||||
buildSnapPoints();
|
||||
getProxy()->repaintNeeded();
|
||||
}
|
||||
|
||||
void PDFPickTool::buildSnapPoints()
|
||||
@ -1030,6 +1119,28 @@ PDFScreenshotTool::PDFScreenshotTool(PDFDrawWidgetProxy* proxy, QAction* action,
|
||||
{
|
||||
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
|
||||
addTool(m_pickTool);
|
||||
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFScreenshotTool::onRectanglePicked);
|
||||
}
|
||||
|
||||
void PDFScreenshotTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
|
||||
{
|
||||
PDFWidgetSnapshot snapshot = getProxy()->getSnapshot();
|
||||
if (const PDFWidgetSnapshot::SnapshotItem* pageSnapshot = snapshot.getPageSnapshot(pageIndex))
|
||||
{
|
||||
QRect selectedRectangle = pageSnapshot->pageToDeviceMatrix.mapRect(pageRectangle).toRect();
|
||||
if (selectedRectangle.isValid())
|
||||
{
|
||||
QImage image(selectedRectangle.size(), QImage::Format_RGB888);
|
||||
|
||||
{
|
||||
QPainter painter(&image);
|
||||
painter.translate(-selectedRectangle.topLeft());
|
||||
getProxy()->drawPages(&painter, getProxy()->getWidget()->rect());
|
||||
}
|
||||
|
||||
QApplication::clipboard()->setImage(image, QClipboard::Clipboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -296,24 +296,35 @@ public:
|
||||
/// \param parent Parent object
|
||||
explicit PDFPickTool(PDFDrawWidgetProxy* proxy, Mode mode, QObject* parent);
|
||||
|
||||
virtual void drawPage(QPainter* painter, PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix) const override;
|
||||
virtual void drawPostRendering(QPainter* painter, QRect rect) const override;
|
||||
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
|
||||
signals:
|
||||
void pointPicked(PDFInteger pageIndex, QPointF pagePoint);
|
||||
void rectanglePicked(PDFInteger pageIndex, QRectF pageRectangle);
|
||||
|
||||
protected:
|
||||
virtual void setActiveImpl(bool active) override;
|
||||
|
||||
private:
|
||||
void resetTool();
|
||||
void buildSnapPoints();
|
||||
|
||||
Mode m_mode;
|
||||
PDFSnapper m_snapper;
|
||||
QPoint m_mousePosition;
|
||||
PDFInteger m_pageIndex;
|
||||
std::vector<QPointF> m_pickedPoints;
|
||||
};
|
||||
|
||||
/// Tool that makes screenshot of page area and copies it to the clipboard
|
||||
/// Tool that makes screenshot of page area and copies it to the clipboard,
|
||||
/// using current client area to determine image size.
|
||||
class PDFFORQTLIBSHARED_EXPORT PDFScreenshotTool : public PDFWidgetTool
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -329,6 +340,8 @@ public:
|
||||
explicit PDFScreenshotTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent);
|
||||
|
||||
private:
|
||||
void onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle);
|
||||
|
||||
PDFPickTool* m_pickTool;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user