diff --git a/PdfForQtLib/sources/pdfadvancedtools.cpp b/PdfForQtLib/sources/pdfadvancedtools.cpp index a23c6ba..4ea9bf0 100644 --- a/PdfForQtLib/sources/pdfadvancedtools.cpp +++ b/PdfForQtLib/sources/pdfadvancedtools.cpp @@ -22,6 +22,7 @@ #include #include +#include namespace pdf { @@ -162,7 +163,7 @@ void PDFCreateFreeTextTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageR QString userName = PDFSysUtils::getUserName(); PDFObjectReference page = getDocument()->getCatalog()->getPage(pageIndex)->getPageReference(); - modifier.getBuilder()->createAnnotationFreeText(page, pageRectangle, PDFSysUtils::getUserName(), QString(), text, TextAlignment(Qt::AlignLeft | Qt::AlignTop)); + modifier.getBuilder()->createAnnotationFreeText(page, pageRectangle, userName, QString(), text, TextAlignment(Qt::AlignLeft | Qt::AlignTop)); modifier.markAnnotationsChanged(); if (modifier.finalize()) @@ -190,4 +191,411 @@ void PDFCreateAnnotationTool::updateActions() } } +PDFCreateLineTypeTool::PDFCreateLineTypeTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, PDFCreateLineTypeTool::Type type, QAction* action, QObject* parent) : + BaseClass(proxy, action, parent), + m_toolManager(toolManager), + m_pickTool(nullptr), + m_type(type), + m_penWidth(1.0), + m_strokeColor(Qt::red), + m_fillColor(Qt::yellow) +{ + m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Points, this); + addTool(m_pickTool); + connect(m_pickTool, &PDFPickTool::pointPicked, this, &PDFCreateLineTypeTool::onPointPicked); + + m_fillColor.setAlphaF(0.2); + + updateActions(); +} + +void PDFCreateLineTypeTool::onPointPicked(PDFInteger pageIndex, QPointF pagePoint) +{ + Q_UNUSED(pageIndex); + Q_UNUSED(pagePoint); + + if (m_type == Type::Line && m_pickTool->getPickedPoints().size() == 2) + { + finishDefinition(); + } +} + +void PDFCreateLineTypeTool::finishDefinition() +{ + const std::vector& pickedPoints = m_pickTool->getPickedPoints(); + + switch (m_type) + { + case Type::Line: + { + if (pickedPoints.size() >= 2) + { + PDFDocumentModifier modifier(getDocument()); + + QString userName = PDFSysUtils::getUserName(); + PDFObjectReference page = getDocument()->getCatalog()->getPage(m_pickTool->getPageIndex())->getPageReference(); + modifier.getBuilder()->createAnnotationLine(page, QRectF(), pickedPoints.front(), pickedPoints.back(), m_penWidth, m_fillColor, m_strokeColor, userName, QString(), QString(), AnnotationLineEnding::None, AnnotationLineEnding::None); + modifier.markAnnotationsChanged(); + + if (modifier.finalize()) + { + emit m_toolManager->documentModified(PDFModifiedDocument(modifier.getDocument(), nullptr, modifier.getFlags())); + } + + setActive(false); + } + break; + } + + case Type::PolyLine: + { + if (pickedPoints.size() >= 3) + { + PDFDocumentModifier modifier(getDocument()); + + QPolygonF polygon; + for (const QPointF& point : pickedPoints) + { + polygon << point; + } + + QString userName = PDFSysUtils::getUserName(); + PDFObjectReference page = getDocument()->getCatalog()->getPage(m_pickTool->getPageIndex())->getPageReference(); + modifier.getBuilder()->createAnnotationPolyline(page, polygon, m_penWidth, m_fillColor, m_strokeColor, userName, QString(), QString(), AnnotationLineEnding::None, AnnotationLineEnding::None); + modifier.markAnnotationsChanged(); + + if (modifier.finalize()) + { + emit m_toolManager->documentModified(PDFModifiedDocument(modifier.getDocument(), nullptr, modifier.getFlags())); + } + + setActive(false); + } + break; + } + + case Type::Polygon: + { + if (pickedPoints.size() >= 3) + { + PDFDocumentModifier modifier(getDocument()); + + QPolygonF polygon; + for (const QPointF& point : pickedPoints) + { + polygon << point; + } + if (!polygon.isClosed()) + { + polygon << pickedPoints.front(); + } + + QString userName = PDFSysUtils::getUserName(); + PDFObjectReference page = getDocument()->getCatalog()->getPage(m_pickTool->getPageIndex())->getPageReference(); + PDFObjectReference annotation = modifier.getBuilder()->createAnnotationPolygon(page, polygon, m_penWidth, m_fillColor, m_strokeColor, userName, QString(), QString()); + modifier.getBuilder()->setAnnotationFillOpacity(annotation, m_fillColor.alphaF()); + modifier.getBuilder()->updateAnnotationAppearanceStreams(annotation); + modifier.markAnnotationsChanged(); + + if (modifier.finalize()) + { + emit m_toolManager->documentModified(PDFModifiedDocument(modifier.getDocument(), nullptr, modifier.getFlags())); + } + + setActive(false); + } + break; + } + + default: + Q_ASSERT(false); + break; + } + + m_pickTool->resetTool(); +} + +QColor PDFCreateLineTypeTool::getFillColor() const +{ + return m_fillColor; +} + +void PDFCreateLineTypeTool::setFillColor(const QColor& fillColor) +{ + m_fillColor = fillColor; +} + +QColor PDFCreateLineTypeTool::getStrokeColor() const +{ + return m_strokeColor; +} + +void PDFCreateLineTypeTool::setStrokeColor(const QColor& strokeColor) +{ + m_strokeColor = strokeColor; +} + +PDFReal PDFCreateLineTypeTool::getPenWidth() const +{ + return m_penWidth; +} + +void PDFCreateLineTypeTool::setPenWidth(PDFReal penWidth) +{ + m_penWidth = penWidth; +} + +void PDFCreateLineTypeTool::keyPressEvent(QWidget* widget, QKeyEvent* event) +{ + Q_UNUSED(widget); + + switch (m_type) + { + case Type::PolyLine: + case Type::Polygon: + { + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) + { + finishDefinition(); + event->accept(); + } + else + { + event->ignore(); + } + + break; + } + + default: + event->ignore(); + break; + } + + if (!event->isAccepted()) + { + BaseClass::keyPressEvent(widget, event); + } +} + +void PDFCreateLineTypeTool::keyReleaseEvent(QWidget* widget, QKeyEvent* event) +{ + Q_UNUSED(widget); + + switch (m_type) + { + case Type::PolyLine: + case Type::Polygon: + { + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) + { + event->accept(); + } + else + { + event->ignore(); + } + + break; + } + + default: + event->ignore(); + break; + } + + if (!event->isAccepted()) + { + BaseClass::keyReleaseEvent(widget, event); + } +} + +void PDFCreateLineTypeTool::drawPage(QPainter* painter, + PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QMatrix& pagePointToDevicePointMatrix, + QList& errors) const +{ + Q_UNUSED(pageIndex); + Q_UNUSED(compiledPage); + Q_UNUSED(layoutGetter); + Q_UNUSED(errors); + + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + + if (pageIndex != m_pickTool->getPageIndex()) + { + return; + } + + const std::vector& points = m_pickTool->getPickedPoints(); + if (points.empty()) + { + return; + } + + QPointF mousePoint = pagePointToDevicePointMatrix.inverted().map(m_pickTool->getSnappedPoint()); + + painter->setWorldMatrix(pagePointToDevicePointMatrix, true); + + QPen pen(m_strokeColor); + QBrush brush(m_fillColor, Qt::SolidPattern); + pen.setWidthF(m_penWidth); + painter->setPen(qMove(pen)); + painter->setBrush(qMove(brush)); + painter->setRenderHint(QPainter::Antialiasing); + + switch (m_type) + { + case Type::Line: + case Type::PolyLine: + { + for (size_t i = 1; i < points.size(); ++i) + { + painter->drawLine(points[i - 1], points[i]); + } + painter->drawLine(points.back(), mousePoint); + break; + } + + case Type::Polygon: + { + QPainterPath path; + path.moveTo(points.front()); + for (size_t i = 1; i < points.size(); ++i) + { + path.lineTo(points[i]); + } + path.lineTo(mousePoint); + path.closeSubpath(); + + painter->drawPath(path); + break; + } + + default: + Q_ASSERT(false); + break; + } +} + +PDFCreateEllipseTool::PDFCreateEllipseTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QAction* action, QObject* parent) : + BaseClass(proxy, action, parent), + m_toolManager(toolManager), + m_pickTool(nullptr), + m_penWidth(1.0), + m_strokeColor(Qt::red), + m_fillColor(Qt::yellow) +{ + m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this); + m_pickTool->setDrawSelectionRectangle(false); + addTool(m_pickTool); + connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFCreateEllipseTool::onRectanglePicked); + + m_fillColor.setAlphaF(0.2); + + updateActions(); +} + +PDFReal PDFCreateEllipseTool::getPenWidth() const +{ + return m_penWidth; +} + +void PDFCreateEllipseTool::setPenWidth(PDFReal penWidth) +{ + m_penWidth = penWidth; +} + +QColor PDFCreateEllipseTool::getStrokeColor() const +{ + return m_strokeColor; +} + +void PDFCreateEllipseTool::setStrokeColor(const QColor& strokeColor) +{ + m_strokeColor = strokeColor; +} + +QColor PDFCreateEllipseTool::getFillColor() const +{ + return m_fillColor; +} + +void PDFCreateEllipseTool::setFillColor(const QColor& fillColor) +{ + m_fillColor = fillColor; +} + +void PDFCreateEllipseTool::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 (pageIndex != m_pickTool->getPageIndex()) + { + return; + } + + const std::vector& points = m_pickTool->getPickedPoints(); + if (points.empty()) + { + return; + } + + QPointF mousePoint = pagePointToDevicePointMatrix.inverted().map(m_pickTool->getSnappedPoint()); + + painter->setWorldMatrix(pagePointToDevicePointMatrix, true); + + QPen pen(m_strokeColor); + QBrush brush(m_fillColor, Qt::SolidPattern); + pen.setWidthF(m_penWidth); + painter->setPen(qMove(pen)); + painter->setBrush(qMove(brush)); + painter->setRenderHint(QPainter::Antialiasing); + + 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)) + { + QRectF rect(xMin, yMin, width, height); + painter->drawEllipse(rect); + } +} + +void PDFCreateEllipseTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle) +{ + if (pageRectangle.isEmpty()) + { + return; + } + + PDFDocumentModifier modifier(getDocument()); + + QString userName = PDFSysUtils::getUserName(); + PDFObjectReference page = getDocument()->getCatalog()->getPage(pageIndex)->getPageReference(); + PDFObjectReference annotation = modifier.getBuilder()->createAnnotationCircle(page, pageRectangle, m_penWidth, m_fillColor, m_strokeColor, userName, QString(), QString()); + modifier.getBuilder()->setAnnotationFillOpacity(annotation, m_fillColor.alphaF()); + modifier.getBuilder()->updateAnnotationAppearanceStreams(annotation); + modifier.markAnnotationsChanged(); + + if (modifier.finalize()) + { + emit m_toolManager->documentModified(PDFModifiedDocument(modifier.getDocument(), nullptr, modifier.getFlags())); + } + + setActive(false); +} + } // namespace pdf diff --git a/PdfForQtLib/sources/pdfadvancedtools.h b/PdfForQtLib/sources/pdfadvancedtools.h index 42b3299..6d27511 100644 --- a/PdfForQtLib/sources/pdfadvancedtools.h +++ b/PdfForQtLib/sources/pdfadvancedtools.h @@ -108,6 +108,89 @@ private: PDFPickTool* m_pickTool; }; +/// Tool that creates line/polyline/polygon annotations. +class PDFFORQTLIBSHARED_EXPORT PDFCreateLineTypeTool : public PDFCreateAnnotationTool +{ + Q_OBJECT + +private: + using BaseClass = PDFCreateAnnotationTool; + +public: + enum class Type + { + Line, + PolyLine, + Polygon + }; + + explicit PDFCreateLineTypeTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, Type type, QAction* action, QObject* parent); + + virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override; + virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override; + virtual void drawPage(QPainter* painter, PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QMatrix& pagePointToDevicePointMatrix, + QList& errors) const override; + + PDFReal getPenWidth() const; + void setPenWidth(PDFReal penWidth); + + QColor getStrokeColor() const; + void setStrokeColor(const QColor& strokeColor); + + QColor getFillColor() const; + void setFillColor(const QColor& fillColor); + +private: + void onPointPicked(PDFInteger pageIndex, QPointF pagePoint); + void finishDefinition(); + + PDFToolManager* m_toolManager; + PDFPickTool* m_pickTool; + Type m_type; + PDFReal m_penWidth; + QColor m_strokeColor; + QColor m_fillColor; +}; + +/// Tool that creates ellipse annotation. +class PDFFORQTLIBSHARED_EXPORT PDFCreateEllipseTool : public PDFCreateAnnotationTool +{ + Q_OBJECT + +private: + using BaseClass = PDFCreateAnnotationTool; + +public: + explicit PDFCreateEllipseTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QAction* action, QObject* parent); + + virtual void drawPage(QPainter* painter, PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QMatrix& pagePointToDevicePointMatrix, + QList& errors) const override; + + PDFReal getPenWidth() const; + void setPenWidth(PDFReal penWidth); + + QColor getStrokeColor() const; + void setStrokeColor(const QColor& strokeColor); + + QColor getFillColor() const; + void setFillColor(const QColor& fillColor); + +private: + void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle); + + PDFToolManager* m_toolManager; + PDFPickTool* m_pickTool; + PDFReal m_penWidth; + QColor m_strokeColor; + QColor m_fillColor; +}; + } // namespace pdf #endif // PDFADVANCEDTOOLS_H diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.cpp b/PdfForQtLib/sources/pdfdocumentbuilder.cpp index fe35d1f..06b20aa 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.cpp +++ b/PdfForQtLib/sources/pdfdocumentbuilder.cpp @@ -4073,6 +4073,21 @@ void PDFDocumentBuilder::setAnnotationContents(PDFObjectReference annotation, } +void PDFDocumentBuilder::setAnnotationFillOpacity(PDFObjectReference annotation, + PDFReal opacity) +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("ca"); + objectBuilder << opacity; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject annotationObject = objectBuilder.takeObject(); + mergeTo(annotation, annotationObject); +} + + void PDFDocumentBuilder::setAnnotationOpacity(PDFObjectReference annotation, PDFReal opacity) { @@ -4512,6 +4527,7 @@ void PDFDocumentBuilder::updateTrailerDictionary(PDFInteger objectCount) updateDocumentInfo(qMove(updatedInfoDictionary)); } + /* END GENERATED CODE */ } // namespace pdf diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.h b/PdfForQtLib/sources/pdfdocumentbuilder.h index 3e2e0b0..9f56d2c 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.h +++ b/PdfForQtLib/sources/pdfdocumentbuilder.h @@ -1161,6 +1161,13 @@ public: QString contents); + /// Sets constant fill opacity of annotation's graphics. + /// \param annotation Annotation + /// \param opacity Opacity (value must be in range from 0.0 to 1.0) + void setAnnotationFillOpacity(PDFObjectReference annotation, + PDFReal opacity); + + /// Sets constant opacity of annotation's graphics. /// \param annotation Annotation /// \param opacity Opacity (value must be in range from 0.0 to 1.0) diff --git a/PdfForQtLib/sources/pdfwidgettool.cpp b/PdfForQtLib/sources/pdfwidgettool.cpp index 9c40789..15b6542 100644 --- a/PdfForQtLib/sources/pdfwidgettool.cpp +++ b/PdfForQtLib/sources/pdfwidgettool.cpp @@ -996,7 +996,8 @@ void PDFMagnifierTool::setMagnifierSize(int magnifierSize) PDFPickTool::PDFPickTool(PDFDrawWidgetProxy* proxy, PDFPickTool::Mode mode, QObject* parent) : BaseClass(proxy, parent), m_mode(mode), - m_pageIndex(-1) + m_pageIndex(-1), + m_drawSelectionRectangle(true) { setCursor((m_mode == Mode::Images) ? Qt::CrossCursor : Qt::BlankCursor); m_snapper.setSnapPointPixelSize(PDFWidgetUtils::scaleDPI_x(proxy->getWidget(), 10)); @@ -1018,7 +1019,7 @@ void PDFPickTool::drawPage(QPainter* painter, Q_UNUSED(errors); // If we are picking rectangles, then draw current selection rectangle - if (m_mode == Mode::Rectangles && m_pageIndex == pageIndex && !m_pickedPoints.empty()) + if (m_mode == Mode::Rectangles && m_drawSelectionRectangle && m_pageIndex == pageIndex && !m_pickedPoints.empty()) { QPoint p1 = pagePointToDevicePointMatrix.map(m_pickedPoints.back()).toPoint(); QPoint p2 = m_snapper.getSnappedPoint().toPoint(); @@ -1206,6 +1207,11 @@ void PDFPickTool::buildSnapData() } } +void PDFPickTool::setDrawSelectionRectangle(bool drawSelectionRectangle) +{ + m_drawSelectionRectangle = drawSelectionRectangle; +} + PDFScreenshotTool::PDFScreenshotTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent) : BaseClass(proxy, action, parent), m_pickTool(nullptr) diff --git a/PdfForQtLib/sources/pdfwidgettool.h b/PdfForQtLib/sources/pdfwidgettool.h index 681052a..2eecf6f 100644 --- a/PdfForQtLib/sources/pdfwidgettool.h +++ b/PdfForQtLib/sources/pdfwidgettool.h @@ -335,6 +335,11 @@ public: void resetTool(); + /// Turns on/off drawing of selection rectangle, if rectangle picking + /// mode is active. + /// \param drawSelectionRectangle Draw selection rectangle? + void setDrawSelectionRectangle(bool drawSelectionRectangle); + signals: void pointPicked(PDFInteger pageIndex, QPointF pagePoint); void rectanglePicked(PDFInteger pageIndex, QRectF pageRectangle); @@ -351,6 +356,7 @@ private: QPoint m_mousePosition; PDFInteger m_pageIndex; std::vector m_pickedPoints; + bool m_drawSelectionRectangle; }; /// Tool that makes screenshot of page area and copies it to the clipboard, diff --git a/PdfForQtViewer/pdfviewermainwindow.cpp b/PdfForQtViewer/pdfviewermainwindow.cpp index b2248b1..e88de7f 100644 --- a/PdfForQtViewer/pdfviewermainwindow.cpp +++ b/PdfForQtViewer/pdfviewermainwindow.cpp @@ -339,6 +339,14 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : m_toolManager->addTool(createHyperlinkTool); pdf::PDFCreateFreeTextTool* createFreeTextTool = new pdf::PDFCreateFreeTextTool(m_pdfWidget->getDrawWidgetProxy(), m_toolManager, ui->actionInlineText, this); m_toolManager->addTool(createFreeTextTool); + pdf::PDFCreateLineTypeTool* createStraightLineTool = new pdf::PDFCreateLineTypeTool(m_pdfWidget->getDrawWidgetProxy(), m_toolManager, pdf::PDFCreateLineTypeTool::Type::Line, ui->actionCreateStraightLine, this); + m_toolManager->addTool(createStraightLineTool); + pdf::PDFCreateLineTypeTool* createPolylineTool = new pdf::PDFCreateLineTypeTool(m_pdfWidget->getDrawWidgetProxy(), m_toolManager, pdf::PDFCreateLineTypeTool::Type::PolyLine, ui->actionCreatePolyline, this); + m_toolManager->addTool(createPolylineTool); + pdf::PDFCreateLineTypeTool* createPolygonTool = new pdf::PDFCreateLineTypeTool(m_pdfWidget->getDrawWidgetProxy(), m_toolManager, pdf::PDFCreateLineTypeTool::Type::Polygon, ui->actionCreatePolygon, this); + m_toolManager->addTool(createPolygonTool); + pdf::PDFCreateEllipseTool* createEllipseTool = new pdf::PDFCreateEllipseTool(m_pdfWidget->getDrawWidgetProxy(), m_toolManager, ui->actionCreateEllipse, this); + m_toolManager->addTool(createEllipseTool); m_annotationManager = new pdf::PDFWidgetAnnotationManager(m_pdfWidget->getDrawWidgetProxy(), this); connect(m_annotationManager, &pdf::PDFWidgetAnnotationManager::actionTriggered, this, &PDFViewerMainWindow::onActionTriggered); diff --git a/PdfForQtViewer/pdfviewermainwindow.ui b/PdfForQtViewer/pdfviewermainwindow.ui index f7c7ecb..0eafe24 100644 --- a/PdfForQtViewer/pdfviewermainwindow.ui +++ b/PdfForQtViewer/pdfviewermainwindow.ui @@ -149,6 +149,10 @@ + + + + @@ -646,6 +650,38 @@ Inline text + + + true + + + Straight Line + + + + + true + + + Polyline + + + + + true + + + Polygon + + + + + true + + + Ellipse + + diff --git a/generated_code_definition.xml b/generated_code_definition.xml index 849e801..2106a88 100644 --- a/generated_code_definition.xml +++ b/generated_code_definition.xml @@ -8579,6 +8579,70 @@ return rootNodeReference; Sets annotation contents. _void + + + + + + + + + + annotation + _PDFObjectReference + Annotation + + + + + opacity + _PDFReal + Opacity (value must be in range from 0.0 to 1.0) + + + Parameters + + _void + + + + + + + + + + + + ca + DictionaryItemSimple + opacity + + + + Dictionary + + + + CreateObject + annotationObject + _PDFObject + + + + + + Code + + _void + mergeTo(annotation, annotationObject); + + + Annotations + setAnnotationFillOpacity + Sets constant fill opacity of annotation's graphics. + _void +