PDF4QT/Pdf4QtLib/sources/pdfpagecontenteditortools.cpp

771 lines
24 KiB
C++
Raw Normal View History

2022-02-11 19:15:57 +01:00
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfpagecontenteditortools.h"
#include "pdfpagecontentelements.h"
2022-02-19 19:30:52 +01:00
#include "pdfpainterutils.h"
2022-03-26 19:26:32 +01:00
#include "pdftexteditpseudowidget.h"
2022-02-11 19:15:57 +01:00
#include <QPen>
2022-02-19 19:30:52 +01:00
#include <QPainter>
2022-02-27 19:59:53 +01:00
#include <QMouseEvent>
2022-03-26 19:26:32 +01:00
#include <QGuiApplication>
2022-02-11 19:15:57 +01:00
namespace pdf
{
PDFCreatePCElementTool::PDFCreatePCElementTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent) :
PDFWidgetTool(proxy, action, parent),
m_scene(scene)
{
}
2022-02-19 19:30:52 +01:00
QRectF PDFCreatePCElementTool::getRectangleFromPickTool(PDFPickTool* pickTool,
const QMatrix& pagePointToDevicePointMatrix)
{
const std::vector<QPointF>& points = pickTool->getPickedPoints();
if (points.empty())
{
return QRectF();
}
QPointF mousePoint = pagePointToDevicePointMatrix.inverted().map(pickTool->getSnappedPoint());
QPointF point = points.front();
qreal xMin = qMin(point.x(), mousePoint.x());
qreal xMax = qMax(point.x(), mousePoint.x());
qreal yMin = qMin(point.y(), mousePoint.y());
qreal yMax = qMax(point.y(), mousePoint.y());
qreal width = xMax - xMin;
qreal height = yMax - yMin;
if (!qFuzzyIsNull(width) && !qFuzzyIsNull(height))
{
return QRectF(xMin, yMin, width, height);
}
return QRectF();
}
2022-02-11 19:15:57 +01:00
PDFCreatePCElementRectangleTool::PDFCreatePCElementRectangleTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
bool isRounded,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_pickTool(nullptr),
m_element(nullptr)
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
m_pickTool->setDrawSelectionRectangle(false);
addTool(m_pickTool);
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFCreatePCElementRectangleTool::onRectanglePicked);
QPen pen(Qt::SolidLine);
pen.setWidthF(1.0);
m_element = new PDFPageContentElementRectangle();
m_element->setBrush(Qt::NoBrush);
m_element->setPen(std::move(pen));
m_element->setRounded(isRounded);
updateActions();
}
PDFCreatePCElementRectangleTool::~PDFCreatePCElementRectangleTool()
{
delete m_element;
}
void PDFCreatePCElementRectangleTool::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 (pageIndex != m_pickTool->getPageIndex())
{
return;
}
2022-02-19 19:30:52 +01:00
QRectF rectangle = getRectangleFromPickTool(m_pickTool, pagePointToDevicePointMatrix);
if (!rectangle.isValid())
2022-02-11 19:15:57 +01:00
{
return;
}
m_element->setPageIndex(pageIndex);
2022-02-19 19:30:52 +01:00
m_element->setRectangle(rectangle);
2022-02-11 19:15:57 +01:00
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
}
void PDFCreatePCElementRectangleTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
{
if (pageRectangle.isEmpty())
{
return;
}
m_element->setPageIndex(pageIndex);
m_element->setRectangle(pageRectangle);
m_scene->addElement(m_element->clone());
setActive(false);
}
2022-02-13 19:46:09 +01:00
PDFCreatePCElementLineTool::PDFCreatePCElementLineTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
bool isHorizontal,
bool isVertical,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_pickTool(nullptr),
m_element(nullptr)
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Points, this);
m_pickTool->setDrawSelectionRectangle(false);
addTool(m_pickTool);
connect(m_pickTool, &PDFPickTool::pointPicked, this, &PDFCreatePCElementLineTool::onPointPicked);
QPen pen(Qt::SolidLine);
pen.setWidthF(2.0);
pen.setCapStyle(Qt::RoundCap);
PDFPageContentElementLine::LineGeometry geometry = PDFPageContentElementLine::LineGeometry::General;
if (isHorizontal)
{
geometry = PDFPageContentElementLine::LineGeometry::Horizontal;
}
if (isVertical)
{
geometry = PDFPageContentElementLine::LineGeometry::Vertical;
}
m_element = new PDFPageContentElementLine();
m_element->setBrush(Qt::NoBrush);
m_element->setPen(std::move(pen));
m_element->setGeometry(geometry);
updateActions();
}
PDFCreatePCElementLineTool::~PDFCreatePCElementLineTool()
{
delete m_element;
}
void PDFCreatePCElementLineTool::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 (pageIndex != m_pickTool->getPageIndex() || !m_startPoint)
{
return;
}
m_element->setPageIndex(pageIndex);
QPointF startPoint = *m_startPoint;
QPointF endPoint = pagePointToDevicePointMatrix.inverted().map(m_pickTool->getSnappedPoint());
QLineF line(startPoint, endPoint);
if (!qFuzzyIsNull(line.length()))
{
m_element->setLine(line);
}
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
}
void PDFCreatePCElementLineTool::clear()
{
m_startPoint = std::nullopt;
}
void PDFCreatePCElementLineTool::onPointPicked(PDFInteger pageIndex, QPointF pagePoint)
{
if (!m_startPoint || m_element->getPageIndex() != pageIndex)
{
m_startPoint = pagePoint;
m_element->setPageIndex(pageIndex);
m_element->setLine(QLineF(pagePoint, pagePoint));
return;
}
if (qFuzzyCompare(m_startPoint.value().x(), pagePoint.x()) &&
qFuzzyCompare(m_startPoint.value().y(), pagePoint.y()))
{
// Jakub Melka: Point is same as the start point
clear();
return;
}
QLineF line = m_element->getLine();
line.setP2(pagePoint);
m_element->setLine(line);
m_scene->addElement(m_element->clone());
clear();
setActive(false);
}
2022-02-19 19:30:52 +01:00
PDFCreatePCElementSvgTool::PDFCreatePCElementSvgTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QByteArray content,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_pickTool(nullptr),
m_element(nullptr)
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
m_pickTool->setDrawSelectionRectangle(false);
addTool(m_pickTool);
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFCreatePCElementSvgTool::onRectanglePicked);
m_element = new PDFPageContentSvgElement();
m_element->setContent(content);
updateActions();
}
PDFCreatePCElementSvgTool::~PDFCreatePCElementSvgTool()
{
delete m_element;
}
void PDFCreatePCElementSvgTool::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 (pageIndex != m_pickTool->getPageIndex())
{
return;
}
QRectF rectangle = getRectangleFromPickTool(m_pickTool, pagePointToDevicePointMatrix);
if (!rectangle.isValid())
{
return;
}
m_element->setPageIndex(pageIndex);
m_element->setRectangle(rectangle);
{
PDFPainterStateGuard guard(painter);
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(Qt::DotLine);
painter->setBrush(Qt::NoBrush);
painter->drawRect(rectangle);
}
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
}
void PDFCreatePCElementSvgTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
{
if (pageRectangle.isEmpty())
{
return;
}
m_element->setPageIndex(pageIndex);
m_element->setRectangle(pageRectangle);
m_scene->addElement(m_element->clone());
setActive(false);
}
2022-02-27 17:08:24 +01:00
PDFCreatePCElementDotTool::PDFCreatePCElementDotTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_pickTool(nullptr),
m_element(nullptr)
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Points, this);
m_pickTool->setDrawSelectionRectangle(false);
addTool(m_pickTool);
connect(m_pickTool, &PDFPickTool::pointPicked, this, &PDFCreatePCElementDotTool::onPointPicked);
QPen pen(Qt::SolidLine);
pen.setWidthF(5.0);
pen.setCapStyle(Qt::RoundCap);
m_element = new PDFPageContentElementDot();
m_element->setBrush(Qt::NoBrush);
m_element->setPen(std::move(pen));
updateActions();
}
PDFCreatePCElementDotTool::~PDFCreatePCElementDotTool()
{
delete m_element;
}
void PDFCreatePCElementDotTool::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);
QPointF point = pagePointToDevicePointMatrix.inverted().map(m_pickTool->getSnappedPoint());
PDFPainterStateGuard guard(painter);
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(m_element->getPen());
painter->setBrush(m_element->getBrush());
painter->drawPoint(point);
}
void PDFCreatePCElementDotTool::onPointPicked(PDFInteger pageIndex, QPointF pagePoint)
{
m_element->setPageIndex(pageIndex);
m_element->setPoint(pagePoint);
m_scene->addElement(m_element->clone());
m_element->setPageIndex(-1);
setActive(false);
}
2022-02-27 19:59:53 +01:00
PDFCreatePCElementFreehandCurveTool::PDFCreatePCElementFreehandCurveTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_element(nullptr)
{
QPen pen(Qt::SolidLine);
pen.setWidthF(2.0);
pen.setCapStyle(Qt::RoundCap);
m_element = new PDFPageContentElementFreehandCurve();
m_element->setBrush(Qt::NoBrush);
m_element->setPen(std::move(pen));
}
PDFCreatePCElementFreehandCurveTool::~PDFCreatePCElementFreehandCurveTool()
{
delete m_element;
}
void PDFCreatePCElementFreehandCurveTool::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 (pageIndex != m_element->getPageIndex() || m_element->isEmpty())
{
return;
}
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
}
void PDFCreatePCElementFreehandCurveTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->accept();
if (event->button() == Qt::LeftButton)
{
// Try to perform pick point
QPointF pagePoint;
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
if (pageIndex != -1 && // We have picked some point on page
(m_element->getPageIndex() == -1 || m_element->getPageIndex() == pageIndex)) // We are under current page
{
m_element->setPageIndex(pageIndex);
m_element->addStartPoint(pagePoint);
}
}
else if (event->button() == Qt::RightButton)
{
resetTool();
}
emit getProxy()->repaintNeeded();
}
void PDFCreatePCElementFreehandCurveTool::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->accept();
if (event->button() == Qt::LeftButton)
{
// Try to perform pick point
QPointF pagePoint;
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
if (pageIndex != -1 && // We have picked some point on page
(m_element->getPageIndex() == pageIndex)) // We are under current page
{
m_element->setPageIndex(pageIndex);
m_element->addPoint(pagePoint);
if (!m_element->isEmpty())
{
m_scene->addElement(m_element->clone());
}
}
resetTool();
}
emit getProxy()->repaintNeeded();
}
void PDFCreatePCElementFreehandCurveTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->accept();
if (event->buttons() & Qt::LeftButton && m_element->getPageIndex() != -1)
{
// Try to add point to the path
QPointF pagePoint;
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
if (pageIndex == m_element->getPageIndex())
{
m_element->addPoint(pagePoint);
}
emit getProxy()->repaintNeeded();
}
}
void PDFCreatePCElementFreehandCurveTool::setActiveImpl(bool active)
{
BaseClass::setActiveImpl(active);
if (!active)
{
resetTool();
}
}
void PDFCreatePCElementFreehandCurveTool::resetTool()
{
m_element->clear();
}
2022-03-26 19:26:32 +01:00
PDFCreatePCElementTextTool::PDFCreatePCElementTextTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_pickTool(nullptr),
m_element(nullptr),
m_textEditWidget(nullptr)
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
m_pickTool->setDrawSelectionRectangle(true);
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFCreatePCElementTextTool::onRectanglePicked);
QFont font = QGuiApplication::font();
font.setPixelSize(16.0);
m_element = new PDFPageContentElementTextBox();
m_element->setBrush(Qt::NoBrush);
m_element->setPen(QPen(Qt::SolidLine));
m_element->setFont(font);
m_textEditWidget = new PDFTextEditPseudowidget(PDFFormField::Multiline);
2022-03-26 19:26:32 +01:00
}
PDFCreatePCElementTextTool::~PDFCreatePCElementTextTool()
{
delete m_textEditWidget;
delete m_element;
}
void PDFCreatePCElementTextTool::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 (pageIndex != m_element->getPageIndex())
{
return;
}
if (isEditing())
{
PDFPainterStateGuard guard(painter);
AnnotationDrawParameters parameters;
parameters.painter = painter;
parameters.boundingRectangle = m_element->getRectangle();
parameters.key.first = PDFAppeareanceStreams::Appearance::Normal;
parameters.invertColors = getProxy()->getFeatures().testFlag(PDFRenderer::InvertColors);
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
m_textEditWidget->draw(parameters, true);
}
}
void PDFCreatePCElementTextTool::resetTool()
{
m_textEditWidget->setText(QString());
m_element->setText(QString());
m_element->setPageIndex(-1);
if (getTopToolstackTool())
{
removeTool();
}
}
2022-03-26 19:26:32 +01:00
void PDFCreatePCElementTextTool::setActiveImpl(bool active)
{
BaseClass::setActiveImpl(active);
if (active)
{
Q_ASSERT(!getTopToolstackTool());
addTool(m_pickTool);
}
else
{
resetTool();
2022-03-26 19:26:32 +01:00
}
m_pickTool->setActive(active);
}
void PDFCreatePCElementTextTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
{
if (pageRectangle.isEmpty())
{
return;
}
m_element->setPageIndex(pageIndex);
m_element->setRectangle(pageRectangle);
m_textEditWidget->setAppearance(m_element->getFont(),
m_element->getAlignment(),
m_element->getRectangle(),
std::numeric_limits<int>::max(),
m_element->getPen().color());
removeTool();
}
void PDFCreatePCElementTextTool::finishEditing()
{
m_element->setText(m_textEditWidget->getText());
if (!m_element->getText().isEmpty())
{
m_scene->addElement(m_element->clone());
}
resetTool();
2022-03-26 19:26:32 +01:00
setActive(false);
}
std::optional<QPointF> PDFCreatePCElementTextTool::getPagePointUnderMouse(QMouseEvent* event) const
{
QPointF pagePoint;
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
if (pageIndex == m_element->getPageIndex() &&
m_element->getRectangle().contains(pagePoint))
{
return pagePoint;
}
return std::nullopt;
}
bool PDFCreatePCElementTextTool::isEditing() const
{
return isActive() && !getTopToolstackTool();
}
void PDFCreatePCElementTextTool::shortcutOverrideEvent(QWidget* widget, QKeyEvent* event)
{
Q_UNUSED(widget);
if (isEditing())
{
m_textEditWidget->shortcutOverrideEvent(widget, event);
}
}
void PDFCreatePCElementTextTool::keyPressEvent(QWidget* widget, QKeyEvent* event)
{
event->ignore();
if (!isEditing())
{
BaseClass::keyPressEvent(widget, event);
return;
}
if (event->key() == Qt::Key_Escape)
{
return;
}
if (!m_textEditWidget->isMultiline() && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return))
{
// Commit the editor and create element
finishEditing();
event->accept();
return;
}
m_textEditWidget->keyPressEvent(widget, event);
if (event->isAccepted())
{
widget->update();
}
}
void PDFCreatePCElementTextTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
{
if (isEditing())
{
if (event->button() == Qt::LeftButton)
{
std::optional<QPointF> pagePoint = getPagePointUnderMouse(event);
if (pagePoint)
{
const int cursorPosition = m_textEditWidget->getCursorPositionFromWidgetPosition(pagePoint.value(), true);
m_textEditWidget->setCursorPosition(cursorPosition, event->modifiers() & Qt::ShiftModifier);
}
else
{
finishEditing();
}
event->accept();
widget->update();
}
}
else
{
BaseClass::mousePressEvent(widget, event);
}
}
void PDFCreatePCElementTextTool::mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event)
{
if (isEditing())
{
if (event->button() == Qt::LeftButton)
{
std::optional<QPointF> pagePoint = getPagePointUnderMouse(event);
if (pagePoint)
{
const int cursorPosition = m_textEditWidget->getCursorPositionFromWidgetPosition(pagePoint.value(), true);
m_textEditWidget->setCursorPosition(cursorPosition, false);
m_textEditWidget->setCursorPosition(m_textEditWidget->getCursorWordBackward(), false);
m_textEditWidget->setCursorPosition(m_textEditWidget->getCursorWordForward(), true);
}
else
{
finishEditing();
}
event->accept();
widget->update();
}
}
else
{
BaseClass::mousePressEvent(widget, event);
}
}
void PDFCreatePCElementTextTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
{
if (isEditing())
{
std::optional<QPointF> pagePoint = getPagePointUnderMouse(event);
if (pagePoint)
{
// We must test, if left mouse button is pressed while
// we are moving the mouse - if yes, then select the text.
if (event->buttons() & Qt::LeftButton)
{
const int cursorPosition = m_textEditWidget->getCursorPositionFromWidgetPosition(pagePoint.value(), true);
m_textEditWidget->setCursorPosition(cursorPosition, true);
event->accept();
widget->update();
}
}
}
else
{
BaseClass::mouseMoveEvent(widget, event);
}
}
void PDFCreatePCElementTextTool::wheelEvent(QWidget* widget, QWheelEvent* event)
{
if (isEditing())
{
event->ignore();
2022-03-26 19:26:32 +01:00
}
else
{
BaseClass::wheelEvent(widget, event);
}
}
2022-02-11 19:15:57 +01:00
} // namespace pdf