mirror of
				https://github.com/JakubMelka/PDF4QT.git
				synced 2025-06-05 21:59:17 +02:00 
			
		
		
		
	Advanced snapping, basics of screenshot tool
This commit is contained in:
		@@ -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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user