Коммит 8ed512f3 создал по автору OMP Education's avatar OMP Education Зафиксировано автором Alexey Andreyev
Просмотр файлов

[Annotations] Implement annotation creation

Contributes to:

https://gitlab.com/omprussia/libraries/amberpdf-qml-plugin/-/issues/13
владелец 35ba91c9
......@@ -59,12 +59,14 @@ Rectangle {
property alias contentY: viewFlick.contentY
property alias bookmarksModel: pdfView.bookmarksModel
property alias fileVersion: pdfView.fileVersion
property alias documentEdited: pdfView.documentEdited
signal clicked()
signal clickedUrl(string url)
signal noteActivate(string noteText, string author)
signal contentChanged()
signal documentSaved(bool saveStatus)
signal holding(var lastMouse, var screenCoordinates)
function correctPosition() {
viewFlick.contentY = pdfView.contentY
......@@ -79,6 +81,11 @@ Rectangle {
function saveDocumentAs(path) {
pdfView.saveDocumentAs(path)
}
function addAnnotation(rect, author, content, color) {
rect.x += viewFlick.contentX
rect.y += viewFlick.contentY
pdfView.addAnnotation(rect, color, author, content)
}
color: "transparent"
data: [
......@@ -230,16 +237,54 @@ Rectangle {
anchors.fill: parent
enabled: parent.enabled
property point lastMouse
property double deltaX: 10.0
property double deltaY: 10.0
property int doubleClickLatency: 350
property int holdLatency: 800
signal click(var lastMouse)
signal doubleClick(var lastMouse)
signal hold(var lastMouse, var screenCoordinates)
onClick: viewFlick.clicked(lastMouse)
onDoubleClick: viewFlick.doubleClicked(lastMouse)
onHold: root.holding(lastMouse, screenCoordinates)
onPressed: {
lastMouse = Qt.point(mouseX, mouseY)
holdTimer.start()
}
onReleased: {
lastMouse = Qt.point(mouseX, mouseY)
if (doubleClickTimer.running) {
doubleClickTimer.stop()
viewFlick.doubleClicked(Qt.point(mouseX, mouseY))
doubleClick(lastMouse)
if (holdTimer.running)
holdTimer.stop()
return
}
doubleClickTimer.lastMouse = Qt.point(mouseX, mouseY)
doubleClickTimer.start()
if (holdTimer.running) {
holdTimer.stop()
doubleClickTimer.start()
}
}
onMouseXChanged: {
if (!holdTimer.running)
return
if (Math.abs(mouseX - lastMouse.x) > deltaX)
holdTimer.stop()
}
onMouseYChanged: {
if (!holdTimer.running)
return
if (Math.abs(mouseY - lastMouse.y) > deltaY)
holdTimer.stop()
}
Timer {
......@@ -247,9 +292,24 @@ Rectangle {
property var lastMouse
interval: 350
interval: mouseArea.doubleClickLatency
onTriggered: viewFlick.clicked(lastMouse)
onTriggered: mouseArea.click(mouseArea.lastMouse)
}
Timer {
id: holdTimer
interval: mouseArea.holdLatency
repeat: false
onTriggered: {
if (doubleClickTimer.running)
doubleClickTimer.stop()
mouseArea.hold(mouseArea.lastMouse,
Qt.point(mouseArea.lastMouse.x - viewFlick.contentX,
mouseArea.lastMouse.y - viewFlick.contentY))
}
}
}
}
......
......@@ -63,10 +63,13 @@ public:
virtual QFuture<QImage> bitmapPart(qreal pageScaleX, qreal pageScaleY,
int renderFlags = 0, qreal zoom = 1.0,
const QPointF &bias = QPointF()) const = 0;
virtual void addAnnotation(const QRectF &rect, const QColor &color,
const QString &author, const QString &content) = 0;
signals:
void annotationsLoaded();
void originalSizeLoaded();
void annotationAdded(bool);
};
#endif // BASEPAGE_H
......@@ -66,3 +66,24 @@ QFuture<QImage> PdfPageItem::bitmapPart(qreal pageScaleX, qreal pageScaleY, int
{
return m_amberPage ? m_amberPage->bitmapPart(pageScaleX, pageScaleY, renderFlags, zoom, bias) : QFuture<QImage>();
}
void TinyPdfPage::addAnnotation(const QRectF &rect, const QColor &color, const QString &author, const QString &content)
{
if (!m_amberPage)
return;
auto *watcher = new QFutureWatcher<bool>();
connect(watcher, &QFutureWatcher<bool>::finished, this, [this, watcher]() {
if (watcher == nullptr)
return;
if (watcher->isFinished() && !watcher->isCanceled())
emit annotationAdded(true);
else
emit annotationAdded(false);
watcher->deleteLater();
});
auto future = m_amberPage->addAnnotation(rect, color, author, content);
watcher->setFuture(future);
}
......@@ -63,6 +63,9 @@ public:
QFuture<QImage> bitmapPart(qreal pageScaleX, qreal pageScaleY,
int renderFlags = 0, qreal zoom = 1.0,
const QPointF &bias = QPointF()) const override;
void addAnnotation(const QRectF &rect, const QColor &color,
const QString &author, const QString &content) override;
private:
QSharedPointer<PdfPage> m_amberPage;
QList<BaseAnnotation *> m_annotations;
......
......@@ -43,6 +43,9 @@
#include <QFutureWatcher>
#include "pdfpagetile.h"
#include "pdfsimpleannotation.h"
#include "pdfsimplenote.h"
#include "baseannotation.h"
#include "basepage.h"
#include "pdfpagecontainer.h"
......@@ -52,6 +55,8 @@ PdfPageContainer::PdfPageContainer(QQuickItem *parent) : QQuickItem(parent),
m_mapper(nullptr),
m_maxTileZ(0),
m_tileSize(256),
m_annotationsPaint(false),
m_notesPaint(false),
m_allTilesReady(false)
{
setFlag(QQuickItem::ItemHasContents, true);
......@@ -74,6 +79,37 @@ void PdfPageContainer::geometryChanged(const QRectF &newGeometry, const QRectF &
if (qFuzzyCompare(newGeometry.width(), oldGeometry.width()) && qFuzzyCompare(newGeometry.height(), oldGeometry.height()))
return;
auto pageRate = width() / m_pageGeometry.width();
QMutableListIterator<PdfSimpleAnnotation *> annotIter(m_annotationsItems);
while (annotIter.hasNext()) {
annotIter.next();
if (annotIter.value() == nullptr) {
annotIter.remove();
continue;
}
auto pdfAnnotation = annotIter.value()->source();
annotIter.value()->setX(pdfAnnotation->rect.x() * pageRate);
annotIter.value()->setY((m_pageGeometry.height() - pdfAnnotation->rect.y()) * pageRate - pdfAnnotation->rect.height() * pageRate);
annotIter.value()->setWidth(pdfAnnotation->rect.width() * pageRate);
annotIter.value()->setHeight(pdfAnnotation->rect.height() * pageRate);
}
QMutableListIterator<PdfSimpleNote *> noteIter(m_notesItems);
while (noteIter.hasNext()) {
noteIter.next();
if (noteIter.value() == nullptr) {
noteIter.remove();
continue;
}
auto pdfAnnotation = noteIter.value()->source();
noteIter.value()->setX(pdfAnnotation->rect.x() * pageRate);
noteIter.value()->setY((m_pageGeometry.height() - pdfAnnotation->rect.y()) * pageRate - pdfAnnotation->rect.height() * pageRate);
noteIter.value()->setWidth(pdfAnnotation->rect.width() * pageRate);
noteIter.value()->setHeight(pdfAnnotation->rect.height() * pageRate);
}
}
QSizeF PdfPageContainer::requestedSize() const
......@@ -118,6 +154,22 @@ void PdfPageContainer::setPageSource(QSharedPointer<BasePage> pageSource)
sizeWatcher->setFuture(m_pageSource->originalSize());
_correctSize();
if (!m_pageSource->isAnnotationsSupport())
return;
connect(m_pageSource.data(), &BasePage::annotationsLoaded, this, &PdfPageContainer::_loadAnnotations);
connect(m_pageSource.data(), &BasePage::annotationAdded, this, [this](bool added) {
if (m_pageSource && added) {
m_pageSource->loadAnnotations();
emit pageChanged();
}
});
if (m_pageSource->annotations().isEmpty())
m_pageSource->loadAnnotations();
else
_loadAnnotations();
}
void PdfPageContainer::setPageGeometry(const PageGeometry &pg)
......@@ -160,10 +212,41 @@ void PdfPageContainer::setVisibleArea(const QRectF &visibleArea)
m_visibleArea = visibleArea;
}
bool PdfPageContainer::annotationsPaint() const
{
return m_annotationsPaint;
}
bool PdfPageContainer::notesPaint() const
{
return m_notesPaint;
}
QSharedPointer<BasePage> PdfPageContainer::source() const {
return m_pageSource;
}
void PdfPageContainer::addAnnotation(const QRectF &rect, const QColor &color, const QString &author, const QString &content)
{
if (!m_pageSource)
return;
auto pageRate = width() / m_pageGeometry.width();
auto correctRect = QRectF();
correctRect.setX(qMax(0.0f, float(rect.x() / pageRate)));
correctRect.setY(qMax(0.0f, float((height() - rect.y() - rect.height()) / pageRate)));
correctRect.setWidth(rect.width() / pageRate);
if (correctRect.x() + correctRect.width() > m_pageGeometry.width())
correctRect.setWidth(m_pageGeometry.width() - correctRect.x() - 1);
correctRect.setHeight(rect.height() / pageRate);
if (correctRect.y() + correctRect.height() > m_pageGeometry.height())
correctRect.setHeight(m_pageGeometry.height() - correctRect.y() - 1);
m_pageSource->addAnnotation(correctRect, color, author, content);
}
void PdfPageContainer::setRequestedSize(QSizeF requestedSize)
{
if (m_requestedSize == requestedSize)
......@@ -196,6 +279,36 @@ void PdfPageContainer::setScale(qreal scale)
_correctSize();
}
void PdfPageContainer::setAnnotationsPaint(bool annotationsPaint)
{
if (m_annotationsPaint == annotationsPaint)
return;
m_annotationsPaint = annotationsPaint;
emit annotationsPaintChanged(m_annotationsPaint);
if (m_annotationsItems.isEmpty())
_loadAnnotations();
else
for (auto annotation : m_annotationsItems)
annotation->setOpacity(m_annotationsPaint ? 1.0 : 0.0);
}
void PdfPageContainer::setNotesPaint(bool notesPaint)
{
if (m_notesPaint == notesPaint)
return;
m_notesPaint = notesPaint;
emit notesPaintChanged(m_notesPaint);
if (m_notesItems.isEmpty())
_loadAnnotations();
else
for (auto note : m_notesItems)
note->setOpacity(m_notesPaint ? 1.0 : 0.0);
}
void PdfPageContainer::_correctSize()
{
auto heightToWidthRatio = m_pageGeometry.height() / m_pageGeometry.width();
......@@ -238,6 +351,7 @@ void PdfPageContainer::_tailorise()
actualTileWidth = m_pageImageSize.width() / maxTileCount;
actualTileHeight = m_pageImageSize.height() / maxTileCount;
auto tileIndex = -1;
auto oldMaxZ = m_maxTileZ;
for (int tileX = 0; tileX < maxTileCount; ++tileX) {
for (int tileY = 0; tileY < maxTileCount; ++tileY) {
++tileIndex;
......@@ -274,6 +388,14 @@ void PdfPageContainer::_tailorise()
}
}
if (qFuzzyCompare(m_maxTileZ, oldMaxZ)) {
for (auto annotationItem : m_annotationsItems)
annotationItem->setZ(m_maxTileZ + 1);
for (auto noteItem : m_notesItems)
noteItem->setZ(m_maxTileZ + 1);
}
emit pageReady();
m_allTilesReady = false;
......@@ -338,6 +460,66 @@ void PdfPageContainer::_updateVisible()
return;
if (allTilesAreBitmap) {
for (auto &annotation : m_annotationsItems)
annotation->setVisible(true);
for (auto &note : m_notesItems)
note->setVisible(true);
m_allTilesReady = true;
}
}
void PdfPageContainer::_loadAnnotations()
{
if (!m_pageSource)
return;
for (auto &annotation : m_annotationsItems)
annotation->deleteLater();
m_annotationsItems.clear();
for (auto &note : m_notesItems)
note->deleteLater();
m_notesItems.clear();
auto pageRate = width() / m_pageGeometry.width();
auto annotations = m_pageSource->annotations();
for (const auto &annotation : annotations) {
if (annotation == nullptr)
continue;
if (annotation->type == BaseAnnotation::AnnotationType::Link || annotation->type == BaseAnnotation::AnnotationType::Url) {
auto annotationItem = new PdfSimpleAnnotation(this, annotation);
connect(annotationItem, &PdfSimpleAnnotation::triggered, this, &PdfPageContainer::annotationActivate);
connect(this, &PdfPageContainer::yChanged, annotationItem, &PdfSimpleAnnotation::clearHighlight);
annotationItem->setOpacity(m_annotationsPaint ? 1.0 : 0.0);
annotationItem->setX(annotation->rect.x() * pageRate);
annotationItem->setY((m_pageGeometry.height() - annotation->rect.y()) * pageRate - annotation->rect.height() * pageRate);
annotationItem->setWidth(annotation->rect.width() * pageRate);
annotationItem->setHeight(annotation->rect.height() * pageRate);
annotationItem->setZ(m_maxTileZ);
annotationItem->setVisible(false);
m_annotationsItems.append(annotationItem);
continue;
}
if (annotation->type == BaseAnnotation::AnnotationType::HighLight
|| annotation->type == BaseAnnotation::AnnotationType::Text) {
auto noteItem = new PdfSimpleNote(this, annotation);
connect(noteItem, &PdfSimpleNote::triggered, this, &PdfPageContainer::noteActivate);
connect(this, &PdfPageContainer::yChanged, noteItem, &PdfSimpleNote::clearHighlight);
noteItem->setOpacity(m_notesPaint ? 1.0 : 0.0);
noteItem->setX(annotation->rect.x() * pageRate);
noteItem->setY((m_pageGeometry.height() - annotation->rect.y()) * pageRate - annotation->rect.height() * pageRate);
noteItem->setWidth(annotation->rect.width() * pageRate);
noteItem->setHeight(annotation->rect.height() * pageRate);
noteItem->setZ(m_maxTileZ);
noteItem->setVisible(false);
m_notesItems.append(noteItem);
continue;
}
}
}
......@@ -74,6 +74,8 @@ public:
static QSizeF pageCurrentSize(const PageGeometry &pageGeometry, const QSizeF &requestedSize, Qt::Orientation orientation, qreal scale);
void setVisibleArea(const QRectF &visibleArea);
QSharedPointer<BasePage> source() const;
void addAnnotation(const QRectF &rect, const QColor &color,
const QString &author, const QString &content);
public slots:
void setRequestedSize(QSizeF requestedSize);
......
......@@ -43,6 +43,7 @@
#include <QtConcurrent/QtConcurrentRun>
#include "basedocument.h"
#include "baseannotation.h"
#include "documentmapper.h"
#include "pdfpagecontainer.h"
#include "bookmarksmodel.h"
......@@ -61,7 +62,9 @@ PdfView::PdfView(QQuickItem *parent) : QQuickItem(parent),
m_catchBound(0),
m_orientation(Qt::Vertical),
m_itemScale(1.0),
m_documentProvider(nullptr)
m_annotationsPaint(false),
m_documentProvider(nullptr),
m_documentEdited(false)
{
setFlag(QQuickItem::ItemHasContents, true);
setFlag(QQuickItem::ItemAcceptsInputMethod, true);
......@@ -215,6 +218,16 @@ qreal PdfView::itemScale() const
return m_itemScale;
}
bool PdfView::annotationsPaint() const
{
return m_annotationsPaint;
}
bool PdfView::notesPaint() const
{
return m_notesPaint;
}
BaseDocument *PdfView::documentProvider() const
{
return m_documentProvider;
......@@ -259,6 +272,47 @@ void PdfView::saveDocumentAs(const QString &path)
}));
}
void PdfView::addAnnotation(const QRectF &rect, const QColor &color, const QString &author, const QString &content)
{
for (const auto &page : m_paintedPages) {
auto pagePosition = m_mapper->actualPagePosition(page);
if (m_orientation == Qt::Vertical) {
if (rect.y() >= pagePosition.start && rect.y() <= pagePosition.end) {
if (!m_pages.contains(page))
return;
QRectF localRect(rect);
localRect.setY(rect.y() - pagePosition.start);
localRect.setHeight(rect.height());
localRect.setWidth(rect.width());
auto pageContainer = qobject_cast<PdfPageContainer *>(m_pages.value(page));
pageContainer->addAnnotation(localRect, color, author, content);
return;
}
} else {
if (rect.x() > pagePosition.start && rect.x() <= pagePosition.end) {
if (!m_pages.contains(page))
return;
QRectF localRect(rect);
localRect.setX(rect.x() - pagePosition.start);
localRect.setHeight(rect.height());
localRect.setWidth(rect.width());
auto pageContainer = qobject_cast<PdfPageContainer *>(m_pages.value(page));
pageContainer->addAnnotation(localRect, color, author, content);
return;
}
}
}
}
bool PdfView::documentEdited() const
{
return m_documentEdited;
}
QString PdfView::pdfPath() const
{
return m_pdfPath;
......@@ -378,6 +432,24 @@ void PdfView::scaleAroundPoint(const QPointF &center, qreal newScale)
emit itemScaleChanged(m_itemScale);
}
void PdfView::setAnnotationsPaint(bool annotationsPaint)
{
if (m_annotationsPaint == annotationsPaint)
return;
m_annotationsPaint = annotationsPaint;
emit annotationsPaintChanged(m_annotationsPaint);
}
void PdfView::setNotesPaint(bool notesPaint)
{
if (m_notesPaint == notesPaint)
return;
m_notesPaint = notesPaint;
emit notesPaintChanged(m_notesPaint);
}
void PdfView::setDocumentProvider(BaseDocument *documentProvider)
{
if (m_documentProvider == documentProvider)
......@@ -683,6 +755,58 @@ void PdfView::_updateCurrentIndex()
}
}
void PdfView::_processActivatedAnnotation(BaseAnnotation *annotation)
{
if (annotation == nullptr)
return;
if (annotation->type == BaseAnnotation::AnnotationType::Url) {
if (!annotation->content.isEmpty())
emit clickedUrl(annotation->content);
return;
}
if (annotation->type != BaseAnnotation::AnnotationType::Link)
return;
auto pageIndex = annotation->linkToPage;
if (pageIndex < 0 && pageIndex >= m_count)
return;
auto pageGeometry = m_mapper->originalPageGeometry(pageIndex);
auto pageCoordinate = annotation->pageCoordinate;
if (pageCoordinate.y() <= 0)
pageCoordinate.setY(0);
if (pageCoordinate.x() <= 0)
pageCoordinate.setX(0);
auto targetPageSize = PdfPageContainer::pageCurrentSize(pageGeometry,
{width(), height()},
m_orientation,
m_itemScale);
auto pageRate = targetPageSize.width() / pageGeometry.width();
QPointF linkOnPagePosition(pageCoordinate.x() * pageRate,
(pageGeometry.height() - pageCoordinate.y()) * pageRate);
auto pagePosition = m_mapper->actualPagePosition(pageIndex);
if (m_orientation == Qt::Vertical) {
if (pageCoordinate.y() <= 0) {
positionViewAtIndex(pageIndex);
} else {
setContentY((pagePosition.start + (pageGeometry.height() - pageCoordinate.y()) * pageRate) * m_itemScale);
}
} else {
setContentX(pagePosition.start * m_itemScale);
}
_positionPages();
emit clickedGoToPage(pageIndex, pageCoordinate);
}
void PdfView::_loadDocument()
{
if (m_pdfPath.isEmpty())
......@@ -696,6 +820,12 @@ void PdfView::_loadDocument()
m_documentProvider->setPath(m_pdfPath);
}
void PdfView::_documentEdited()
{
m_documentEdited = true;
emit documentEditedChanged(true);
}
void PdfView::_preparePages()
{
if (m_paintedPages.isEmpty())
......@@ -723,6 +853,11 @@ void PdfView::_preparePages()
connect(page, &PdfPageContainer::pageReady, this, &PdfView::_positionPages);
connect(this, &PdfView::orientationChanged, page, &PdfPageContainer::setOrientation);
connect(this, &PdfView::itemScaleChanged, page, &PdfPageContainer::setScale);
connect(this, &PdfView::annotationsPaintChanged, page, &PdfPageContainer::setAnnotationsPaint);
connect(page, &PdfPageContainer::annotationActivate, this, &PdfView::_processActivatedAnnotation);
connect(this, &PdfView::notesPaintChanged, page, &PdfPageContainer::setNotesPaint);
connect(page, &PdfPageContainer::noteActivate, this, &PdfView::noteActivated);
connect(page, &PdfPageContainer::pageChanged, this, &PdfView::_documentEdited);
page->setVisible(false);
page->setPageGeometry(m_mapper->originalPageGeometry(pageIndex));
......@@ -730,6 +865,8 @@ void PdfView::_preparePages()
page->setRequestedSize({ width(), height() });
page->setScale(m_itemScale);
page->setMapper(m_mapper);
page->setAnnotationsPaint(m_annotationsPaint);
page->setNotesPaint(m_notesPaint);
auto pageSource = baseDocument->loadPage(pageIndex);
if (pageSource)
......
......@@ -69,6 +69,7 @@ class PdfView : public QQuickItem
Q_PROPERTY(qreal contentTopMargin READ contentTopMargin NOTIFY contentTopMarginChanged)
Q_PROPERTY(BookmarksModel* bookmarksModel READ bookmarksModel NOTIFY bookmarksModelChanged)
Q_PROPERTY(int fileVersion READ fileVersion NOTIFY fileVersionChanged)
Q_PROPERTY(bool documentEdited READ documentEdited NOTIFY documentEditedChanged)
public:
explicit PdfView(QQuickItem *parent = nullptr);
......@@ -97,6 +98,11 @@ public:
int fileVersion() const;
Q_INVOKABLE void saveDocumentAs(const QString &path);
bool documentEdited() const;
Q_INVOKABLE void addAnnotation(const QRectF &rect,
const QColor &color,
const QString &author,
const QString &content);
public slots:
void setContentY(qreal contentY);
......@@ -137,6 +143,7 @@ signals:
void bookmarksModelChanged(BookmarksModel *bookmarksModel);
void fileVersionChanged(int fileVersion);
void documentSaved(bool saveStatus);
void documentEditedChanged(bool documentEdited);
private slots:
void _updateContentSize();
......@@ -148,6 +155,7 @@ private slots:
void _updateCurrentIndex();
void _processActivatedAnnotation(BaseAnnotation *annotation);
void _loadDocument();
void _documentEdited();
private:
DocumentMapper *m_mapper;
......@@ -173,6 +181,7 @@ private:
qreal m_contentTopMargin;
QTimer *m_timer;
BookmarksModel *m_bookmarksModel;
bool m_documentEdited;
};
#endif // PDFVIEW_H
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать