From ee8a0cdccca3e049302a6d67ce1fee09d9a1eaf0 Mon Sep 17 00:00:00 2001 From: Damien Caliste Date: Wed, 28 Feb 2018 08:45:14 +0100 Subject: [PATCH 1/4] [sailfish-office] Allow rotated pages when rendering. --- pdf/pdfcanvas.cpp | 210 ++++++++++++++++++++++++++++------ pdf/pdfcanvas.h | 16 +++ pdf/pdfdocument.cpp | 5 +- pdf/pdfdocument.h | 5 +- pdf/pdfjob.cpp | 21 +++- pdf/pdfjob.h | 6 +- pdf/pdfselection.cpp | 15 ++- pdf/pdfselection.h | 8 +- plugin/PDFSelectionHandle.qml | 6 +- plugin/PDFSelectionView.qml | 4 +- 10 files changed, 235 insertions(+), 61 deletions(-) diff --git a/pdf/pdfcanvas.cpp b/pdf/pdfcanvas.cpp index c2fea683..22f73ac5 100644 --- a/pdf/pdfcanvas.cpp +++ b/pdf/pdfcanvas.cpp @@ -72,6 +72,7 @@ class PDFCanvas::Private , resizeTimer(nullptr) , spacing(10.f) , linkWiggle(4.f) + , pageRotation(Poppler::Page::Rotate0) { } enum TextureType{ @@ -97,6 +98,8 @@ class PDFCanvas::Private float spacing; float linkWiggle; + Poppler::Page::Rotation pageRotation; + QRectF visibleArea; QList pageSizes; @@ -147,8 +150,85 @@ class PDFCanvas::Private page.requested = false; } } -}; + QRectF pageToCanvas(const QRectF &page, const QRectF &rect, bool translated = true) const + { + if (pageRotation == Poppler::Page::Rotate0) { + return QRectF(rect.x() * page.width() + (translated ? page.x() : 0.), + rect.y() * page.height() + (translated ? page.y() : 0.), + rect.width() * page.width(), rect.height() * page.height()); + } else if (pageRotation == Poppler::Page::Rotate90) { + return QRectF((1. - rect.y() - rect.height()) * page.width() + + (translated ? page.x() : 0.), + rect.x() * page.height() + (translated ? page.y() : 0.), + rect.height() * page.width(), rect.width() * page.height()); + } else if (pageRotation == Poppler::Page::Rotate180) { + return QRectF((1. - rect.x() - rect.width()) * page.width() + (translated ? page.x() : 0.), + (1. - rect.y() - rect.height()) * page.height() + (translated ? page.y() : 0.), + rect.width() * page.width(), rect.height() * page.height()); + } else if (pageRotation == Poppler::Page::Rotate270) { + return QRectF(rect.y() * page.width() + (translated ? page.x() : 0.), + (1. - rect.x() - rect.width()) * page.height() + + (translated ? page.y() : 0.), + rect.height() * page.width(), rect.width() * page.height()); + } else { + return QRectF(); + } + } + + QPointF pageToCanvas(const QRectF &page, const QPointF &at, bool translated = true) const + { + if (pageRotation == Poppler::Page::Rotate0) { + return QPointF(at.x() * page.width() + (translated ? page.x() : 0.), + at.y() * page.height() + (translated ? page.y() : 0.)); + } else if (pageRotation == Poppler::Page::Rotate90) { + return QPointF((1. - at.y()) * page.width() + (translated ? page.x() : 0.), + at.x() * page.height() + (translated ? page.y() : 0.)); + } else if (pageRotation == Poppler::Page::Rotate180) { + return QPointF((1. - at.x()) * page.width() + (translated ? page.x() : 0.), + (1. - at.y()) * page.height() + (translated ? page.y() : 0.)); + } else if (pageRotation == Poppler::Page::Rotate270) { + return QPointF(at.y() * page.width() + (translated ? page.x() : 0.), + (1. - at.x()) * page.height() + (translated ? page.y() : 0.)); + } else { + return QPointF(); + } + } + + QSizeF pageToCanvas(const QRectF &page, const QSizeF &size) const + { + if (pageRotation == Poppler::Page::Rotate0) { + return QSizeF(size.width() * page.width(), size.height() * page.height()); + } else if (pageRotation == Poppler::Page::Rotate90) { + return QSizeF(size.height() * page.height(), size.width() * page.width()); + } else if (pageRotation == Poppler::Page::Rotate180) { + return QSizeF(size.width() * page.width(), size.height() * page.height()); + } else if (pageRotation == Poppler::Page::Rotate270) { + return QSizeF(size.height() * page.height(), size.width() * page.width()); + } else { + return QSizeF(); + } + } + + QPointF canvasToPage(const QRectF &page, const QPointF &at, bool absolute = true) const + { + if (pageRotation == Poppler::Page::Rotate0) { + return QPointF((at.x() - (absolute ? page.x() : 0.)) / page.width(), + (at.y() - (absolute ? page.y() : 0.)) / page.height()); + } else if (pageRotation == Poppler::Page::Rotate90) { + return QPointF((at.y() - (absolute ? page.y() : 0.)) / page.height(), + 1. - (at.x() - (absolute ? page.x() : 0.)) / page.width()); + } else if (pageRotation == Poppler::Page::Rotate180) { + return QPointF(1. - (at.x() - (absolute ? page.x() : 0.)) / page.width(), + 1. - (at.y() - (absolute ? page.y() : 0.)) / page.height()); + } else if (pageRotation == Poppler::Page::Rotate270) { + return QPointF(1. - (at.y()- (absolute ? page.y() : 0.)) / page.height(), + (at.x() - (absolute ? page.x() : 0.)) / page.width()); + } else { + return QPointF(); + } + } +}; PDFCanvas::PDFCanvas(QQuickItem *parent) : QQuickItem(parent), d(new Private(this)) @@ -286,6 +366,66 @@ void PDFCanvas::setPagePlaceholderColor(const QColor &color) } } +PDFCanvas::PageRotation PDFCanvas::pageRotation() const +{ + switch (d->pageRotation) { + case (Poppler::Page::Rotate0): + return PDFCanvas::NoRotation; + case (Poppler::Page::Rotate90): + return PDFCanvas::Clockwise; + case (Poppler::Page::Rotate270): + return PDFCanvas::CounterClockwise; + case (Poppler::Page::Rotate180): + return PDFCanvas::UpSideDown; + } + return PDFCanvas::NoRotation; +} + +void PDFCanvas::setPageRotation(PDFCanvas::PageRotation pageRotation) +{ + Poppler::Page::Rotation rotation = Poppler::Page::Rotate0; + switch (pageRotation) { + case (PDFCanvas::NoRotation): + rotation = Poppler::Page::Rotate0; + break; + case (PDFCanvas::Clockwise): + rotation = Poppler::Page::Rotate90; + break; + case (PDFCanvas::CounterClockwise): + rotation = Poppler::Page::Rotate270; + break; + case (PDFCanvas::UpSideDown): + rotation = Poppler::Page::Rotate180; + break; + } + if (d->pageRotation == rotation) + return; + + d->pageRotation = rotation; + for (int i = 0; i < d->pageCount; ++i) + d->cleanPageTexturesLater(d->pages[i]); + layout(); + emit pageRotationChanged(); +} + +void PDFCanvas::rotate(bool clockwise) +{ + switch (d->pageRotation) { + case (Poppler::Page::Rotate0): + setPageRotation(clockwise ? PDFCanvas::Clockwise : PDFCanvas::CounterClockwise); + break; + case (Poppler::Page::Rotate90): + setPageRotation(clockwise ? PDFCanvas::UpSideDown : PDFCanvas::NoRotation); + break; + case (Poppler::Page::Rotate180): + setPageRotation(clockwise ? PDFCanvas::CounterClockwise : PDFCanvas::Clockwise); + break; + case (Poppler::Page::Rotate270): + setPageRotation(clockwise ? PDFCanvas::NoRotation : PDFCanvas::UpSideDown); + break; + } +} + void PDFCanvas::layout() { if (d->pageSizes.count() == 0) { @@ -303,7 +443,12 @@ void PDFCanvas::layout() PDFPage page; page.index = i; - page.rect = QRectF(0, totalHeight, width(), width() * ratio); + if (d->pageRotation == Poppler::Page::Rotate90 || + d->pageRotation == Poppler::Page::Rotate270) { + page.rect = QRectF(0, totalHeight, width(), width() / ratio); + } else { + page.rect = QRectF(0, totalHeight, width(), width() * ratio); + } page.requested = false; // We're cancelling all requests below if (d->pages.contains(i)) { page.renderWidth = d->pages.value(i).renderWidth; @@ -335,12 +480,7 @@ qreal PDFCanvas::squaredDistanceFromRect(const QRectF &pageRect, const QPointF &point) const { qreal dist = 0.; - QRectF rect { - reducedCoordRect.x() * pageRect.width(), - reducedCoordRect.y() * pageRect.height() + pageRect.y(), - reducedCoordRect.width() * pageRect.width(), - reducedCoordRect.height() * pageRect.height() - }; + QRectF rect = d->pageToCanvas(pageRect, reducedCoordRect); if ((qreal)point.x() < rect.left()) { dist += (rect.left() - (qreal)point.x()) * (rect.left() - (qreal)point.x()); @@ -437,11 +577,7 @@ QRectF PDFCanvas::fromPageToItem(int index, const QRectF &rect) const if (index < 0 || index >= d->pageCount) return QRectF(); - const PDFPage &page = d->pages.value(index); - return QRectF(rect.x() * page.rect.width() + page.rect.x(), - rect.y() * page.rect.height() + page.rect.y(), - rect.width() * page.rect.width(), - rect.height() * page.rect.height()); + return d->pageToCanvas(d->pages.value(index).rect, rect); } QPointF PDFCanvas::fromPageToItem(int index, const QPointF &point) const @@ -449,9 +585,20 @@ QPointF PDFCanvas::fromPageToItem(int index, const QPointF &point) const if (index < 0 || index >= d->pageCount) return QPointF(); - const PDFPage &page = d->pages.value(index); - return QPointF(point.x() * page.rect.width() + page.rect.x(), - point.y() * page.rect.height() + page.rect.y()); + return d->pageToCanvas(d->pages.value(index).rect, point); +} + +QSizeF PDFCanvas::fromPageToItem(int index, const QSizeF &size) const +{ + if (index < 0 || index >= d->pageCount) + return QSizeF(); + + return d->pageToCanvas(d->pages.value(index).rect, size); +} + +QPointF PDFCanvas::fromItemToPage(const QRectF &page, const QPointF &point) const +{ + return d->canvasToPage(page, point); } void PDFCanvas::linksFinished(int id, const QList > &links) @@ -483,12 +630,12 @@ void PDFCanvas::pageModified(int id, const QRectF &subpart) } else { int buf = 10; // Ask only for a patch on this page. - QRect request(int(subpart.x() * page.rect.width()) - buf, - int(subpart.y() * page.rect.height()) - buf, - qCeil(subpart.width() * page.rect.width()) + buf * 2, - qCeil(subpart.height() * page.rect.height()) + buf * 2); + QRectF shape(d->pageToCanvas(page.rect, subpart, false)); + QRect request(int(shape.x()) - buf, int(shape.y()) - buf, + qCeil(shape.width()) + buf * 2, qCeil(shape.height()) + buf * 2); d->document->requestPage(id, d->renderWidth, window(), - request, PDFCanvas::Private::PatchTexture); + request, d->pageRotation, + PDFCanvas::Private::PatchTexture); } } @@ -558,8 +705,8 @@ QSGNode* PDFCanvas::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeDa //Visible area equals flickable translated by contentX/Y QRectF visibleArea{ d->flickable->property("contentX").toFloat(), - d->flickable->property("contentY").toFloat(), - d->flickable->width(), d->flickable->height() }; + d->flickable->property("contentY").toFloat(), + d->flickable->width(), d->flickable->height() }; //Loaded area equals visible area scaled to five times the size QRectF loadedArea = { @@ -622,7 +769,8 @@ QSGNode* PDFCanvas::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeDa QRect request = (fullPageFit) ? QRect() : textureLimit; if (!page.requested) { d->document->requestPage(i, d->renderWidth, window(), - request, PDFCanvas::Private::RootTexture); + request, d->pageRotation, + PDFCanvas::Private::RootTexture); page.requested = true; } priorityRequests << QPair >(i, QPair(d->renderWidth, request)); @@ -633,7 +781,8 @@ QSGNode* PDFCanvas::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeDa // We preload full page only if they can fit into texture. if (textureLimit.contains(pageRect)) { d->document->requestPage(i, d->renderWidth, window(), - QRect(), PDFCanvas::Private::RootTexture); + QRect(), d->pageRotation, + PDFCanvas::Private::RootTexture); page.requested = true; } } else if (!loadPage) { @@ -654,7 +803,7 @@ QSGNode* PDFCanvas::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeDa } QMatrix4x4 m; - m.translate(0, page.rect.y()); + m.translate(page.rect.x(), page.rect.y()); t->setMatrix(m); if (showPage) { @@ -728,14 +877,7 @@ QSGNode* PDFCanvas::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeDa rn->setFlag(QSGNode::OwnedByParent); n->appendChildNode(rn); } - QRectF linkRect = page.links.value(l).first; - QRectF targetRect{ - linkRect.x() * page.rect.width(), - linkRect.y() * page.rect.height(), - linkRect.width() * page.rect.width(), - linkRect.height() * page.rect.height() - }; - rn->setRect(targetRect); + rn->setRect(d->pageToCanvas(page.rect, page.links.value(l).first, false)); rn->setColor(d->linkColor); rn = static_cast(rn->nextSibling()); diff --git a/pdf/pdfcanvas.h b/pdf/pdfcanvas.h index b6a0ddf6..4aeac757 100644 --- a/pdf/pdfcanvas.h +++ b/pdf/pdfcanvas.h @@ -29,6 +29,7 @@ class PDFDocument; class PDFCanvas : public QQuickItem { Q_OBJECT + Q_ENUMS(PageRotation) Q_PROPERTY(PDFDocument* document READ document WRITE setDocument NOTIFY documentChanged) Q_PROPERTY(QQuickItem* flickable READ flickable WRITE setFlickable NOTIFY flickableChanged) Q_PROPERTY(float spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) @@ -36,8 +37,16 @@ class PDFCanvas : public QQuickItem Q_PROPERTY(QColor pagePlaceholderColor READ pagePlaceholderColor WRITE setPagePlaceholderColor NOTIFY pagePlaceholderColorChanged) Q_PROPERTY(int currentPage READ currentPage NOTIFY currentPageChanged) Q_PROPERTY(float linkWiggle READ linkWiggle WRITE setLinkWiggle NOTIFY linkWiggleChanged) + Q_PROPERTY(PageRotation pageRotation READ pageRotation WRITE setPageRotation NOTIFY pageRotationChanged) public: + enum PageRotation { + NoRotation, + Clockwise, + CounterClockwise, + UpSideDown + }; + PDFCanvas(QQuickItem *parent = 0); ~PDFCanvas(); @@ -85,6 +94,10 @@ class PDFCanvas : public QQuickItem */ int currentPage() const; + PageRotation pageRotation() const; + void setPageRotation(PageRotation pageRotation); + Q_INVOKABLE void rotate(bool clockwise); + void layout(); /** @@ -99,6 +112,8 @@ class PDFCanvas : public QQuickItem */ Q_INVOKABLE QRectF fromPageToItem(int index, const QRectF &rect) const; Q_INVOKABLE QPointF fromPageToItem(int index, const QPointF &point) const; + QSizeF fromPageToItem(int index, const QSizeF &size) const; + QPointF fromItemToPage(const QRectF &page, const QPointF &point) const; /** * Provide a distance measure from @point to a rectangle given by @reducedCoordRect. * @point is given in PDFCanvas coordinates, while @reducedCoordRect is in @@ -118,6 +133,7 @@ class PDFCanvas : public QQuickItem void currentPageChanged(); void pageLayoutChanged(); void linkWiggleChanged(); + void pageRotationChanged(); protected: virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); diff --git a/pdf/pdfdocument.cpp b/pdf/pdfdocument.cpp index ffe67c03..3cc8b769 100644 --- a/pdf/pdfdocument.cpp +++ b/pdf/pdfdocument.cpp @@ -216,12 +216,13 @@ void PDFDocument::requestLinksAtPage(int page) } void PDFDocument::requestPage(int index, int size, QQuickWindow *window, - QRect subpart, int extraData) + QRect subpart, Poppler::Page::Rotation rotation, int extraData) { if (!isLoaded() || isLocked()) return; - RenderPageJob* job = new RenderPageJob(index, size, window, subpart, extraData); + RenderPageJob* job = new RenderPageJob(index, size, window, subpart, + extraData, rotation); d->thread->queueJob(job); } diff --git a/pdf/pdfdocument.h b/pdf/pdfdocument.h index 418f7e8c..888257fb 100644 --- a/pdf/pdfdocument.h +++ b/pdf/pdfdocument.h @@ -83,8 +83,9 @@ public Q_SLOTS: void setAutoSavePath(const QString &filename); void requestUnLock(const QString &password); void requestLinksAtPage(int page); - void requestPage(int index, int size, QQuickWindow *window, - QRect subpart = QRect(), int extraData = 0); + void requestPage(int index, int size, QQuickWindow *window, QRect subpart = QRect(), + Poppler::Page::Rotation rotation = Poppler::Page::Rotate0, + int extraData = 0); void prioritizeRequest(int index, int size, QRect subpart = QRect()); void cancelPageRequest(int index); void requestPageSizes(); diff --git a/pdf/pdfjob.cpp b/pdf/pdfjob.cpp index e34aa5b0..fae9a506 100644 --- a/pdf/pdfjob.cpp +++ b/pdf/pdfjob.cpp @@ -106,8 +106,15 @@ void LinksJob::run() } RenderPageJob::RenderPageJob(int index, uint width, QQuickWindow *window, - QRect subpart, int extraData) - : PDFJob(PDFJob::RenderPageJob), m_index(index), m_subpart(subpart), m_page(0), m_extraData(extraData), m_window(window), m_width(width) + QRect subpart, int extraData, Poppler::Page::Rotation rotation) + : PDFJob(PDFJob::RenderPageJob) + , m_index(index) + , m_subpart(subpart) + , m_page(0) + , m_extraData(extraData) + , m_window(window) + , m_width(width) + , m_rotation(rotation) { } @@ -117,18 +124,22 @@ void RenderPageJob::run() Poppler::Page *page = m_document->page(m_index); QSizeF size = page->pageSizeF(); + if (m_rotation == Poppler::Page::Rotate90 + || m_rotation == Poppler::Page::Rotate270) { + size.transpose(); + } float scale = 72.0f * (float(m_width) / size.width()); QImage image; if (m_subpart.isEmpty()) { - image = page->renderToImage(scale, scale); + image = page->renderToImage(scale, scale, -1, -1, -1, -1, m_rotation); m_subpart.setCoords(0, 0, image.width(), image.height()); } else { - QRect pageRect = {0, 0, int(m_width), qCeil(size.height() / size.width() * m_width)}; + QRect pageRect(0, 0, int(m_width), qCeil(size.height() / size.width() * m_width)); m_subpart = m_subpart.intersected(pageRect); image = page->renderToImage(scale, scale, m_subpart.x(), m_subpart.y(), - m_subpart.width(), m_subpart.height()); + m_subpart.width(), m_subpart.height(), m_rotation); } // Note: assuming there's exactly one handler (PDFCanvas) to catch ownership of this when PDFDocument emits a signal with this m_page = m_window->createTextureFromImage(image); diff --git a/pdf/pdfjob.h b/pdf/pdfjob.h index c55c9ec2..05735cf3 100644 --- a/pdf/pdfjob.h +++ b/pdf/pdfjob.h @@ -24,6 +24,8 @@ #include #include +#include + namespace Poppler { class Document; @@ -98,7 +100,8 @@ class RenderPageJob : public PDFJob Q_OBJECT public: RenderPageJob(int index, uint width, QQuickWindow *window, - QRect subpart = QRect(), int extraData = 0); + QRect subpart = QRect(), int extraData = 0, + Poppler::Page::Rotation rotation = Poppler::Page::Rotate0); virtual void run(); @@ -113,6 +116,7 @@ class RenderPageJob : public PDFJob private: QQuickWindow *m_window; uint m_width; + Poppler::Page::Rotation m_rotation; }; class PageSizesJob : public PDFJob diff --git a/pdf/pdfselection.cpp b/pdf/pdfselection.cpp index 55b142b9..46fc0787 100644 --- a/pdf/pdfselection.cpp +++ b/pdf/pdfselection.cpp @@ -276,8 +276,7 @@ void PDFSelection::Private::textBoxAtPoint(const QPointF &point, Position positi // Find the first box index in pageIndex that is after @point, // including at @point. If none is found in this page, returns // the first box of the next page. - QPointF reducedCoordPoint {point.x() / at.second.width(), - (point.y() - at.second.y()) / at.second.height()}; + QPointF reducedCoordPoint = canvas->fromItemToPage(at.second, point); int index = 0; PDFSelection::Private::Position previousBoxPosition = PDFSelection::Private::Before; @@ -418,12 +417,12 @@ QPointF PDFSelection::handle1() const return d->canvas->fromPageToItem(d->pageIndexStart, d->start); } -float PDFSelection::handle1Height() const +QSizeF PDFSelection::handle1Size() const { if (d->handleReversed) - return d->canvas->fromPageToItem(d->pageIndexStop, rectAt(-1).second).height(); + return d->canvas->fromPageToItem(d->pageIndexStop, QSizeF(0, rectAt(-1).second.height())); else - return d->canvas->fromPageToItem(d->pageIndexStart, rectAt(0).second).height(); + return d->canvas->fromPageToItem(d->pageIndexStart, QSizeF(0, rectAt(0).second.height())); } void PDFSelection::setHandle1(const QPointF &point) @@ -499,12 +498,12 @@ QPointF PDFSelection::handle2() const return d->canvas->fromPageToItem(d->pageIndexStop, d->stop); } -float PDFSelection::handle2Height() const +QSizeF PDFSelection::handle2Size() const { if (d->handleReversed) - return d->canvas->fromPageToItem(d->pageIndexStart, rectAt(0).second).height(); + return d->canvas->fromPageToItem(d->pageIndexStart, QSizeF(0, rectAt(0).second.height())); else - return d->canvas->fromPageToItem(d->pageIndexStop, rectAt(-1).second).height(); + return d->canvas->fromPageToItem(d->pageIndexStop, QSizeF(0, rectAt(-1).second.height())); } void PDFSelection::setHandle2(const QPointF &point) diff --git a/pdf/pdfselection.h b/pdf/pdfselection.h index 929e5670..c47c46c6 100644 --- a/pdf/pdfselection.h +++ b/pdf/pdfselection.h @@ -30,8 +30,8 @@ class PDFSelection : public QAbstractListModel Q_PROPERTY(PDFCanvas* canvas READ canvas WRITE setCanvas NOTIFY canvasChanged) Q_PROPERTY(QPointF handle1 READ handle1 WRITE setHandle1 NOTIFY handle1Changed) Q_PROPERTY(QPointF handle2 READ handle2 WRITE setHandle2 NOTIFY handle2Changed) - Q_PROPERTY(float handle1Height READ handle1Height NOTIFY handle1Changed) - Q_PROPERTY(float handle2Height READ handle2Height NOTIFY handle2Changed) + Q_PROPERTY(QSizeF handle1Size READ handle1Size NOTIFY handle1Changed) + Q_PROPERTY(QSizeF handle2Size READ handle2Size NOTIFY handle2Changed) Q_PROPERTY(QString text READ text NOTIFY textChanged) Q_PROPERTY(float wiggle READ wiggle WRITE setWiggle NOTIFY wiggleChanged) @@ -73,7 +73,7 @@ class PDFSelection : public QAbstractListModel * This handle can be dragged later and become the stop handle. */ QPointF handle1() const; - float handle1Height() const; + QSizeF handle1Size() const; /** * Change the start/stop of the selection to the start/end of the word at point. * point is given in canvas coordinates. If there is no word at point, @@ -86,7 +86,7 @@ class PDFSelection : public QAbstractListModel * This handle can later be dragged to become the start handle. */ QPointF handle2() const; - float handle2Height() const; + QSizeF handle2Size() const; /** * Change the stop of the selection to the end of the word at point. * point is given in canvas coordinates. If there is no word at point, diff --git a/plugin/PDFSelectionHandle.qml b/plugin/PDFSelectionHandle.qml index ffb6db6a..44e8c35a 100644 --- a/plugin/PDFSelectionHandle.qml +++ b/plugin/PDFSelectionHandle.qml @@ -24,8 +24,8 @@ Rectangle { property alias attachX: translationMove.from property point handle + property size handleSize property bool dragged - property real dragHeight x: handle.x - width / 2 y: handle.y - height / 2 @@ -40,8 +40,8 @@ Rectangle { name: "dragged" PropertyChanges { target: root - width: Theme.paddingSmall / 2 - height: dragHeight + width: handleSize.width > 0 ? handleSize.width : (Theme.paddingSmall / 2) + height: handleSize.height > 0 ? handleSize.height : (Theme.paddingSmall / 2) radius: 0 } } diff --git a/plugin/PDFSelectionView.qml b/plugin/PDFSelectionView.qml index a690b9c1..1937ec6a 100644 --- a/plugin/PDFSelectionView.qml +++ b/plugin/PDFSelectionView.qml @@ -46,7 +46,7 @@ Repeater { ? flickable.contentX : handle.x - Theme.itemSizeExtraLarge handle: root.model.handle1 - dragHeight: root.model.handle1Height + handleSize: root.model.handle1Size }, PDFSelectionHandle { id: handle2 @@ -55,7 +55,7 @@ Repeater { ? flickable.contentX + flickable.width : handle.x + Theme.itemSizeExtraLarge handle: root.model.handle2 - dragHeight: root.model.handle2Height + handleSize: root.model.handle2Size } ] } From 5b5a3efdf940305d1ef6c6f67baf29438956b4e4 Mon Sep 17 00:00:00 2001 From: Damien Caliste Date: Thu, 15 Mar 2018 20:26:47 +0100 Subject: [PATCH 2/4] [sailfish-office] Add a rotation command in the UI. --- plugin/PDFDocumentPage.qml | 17 +++++++++++++++++ plugin/PDFView.qml | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/plugin/PDFDocumentPage.qml b/plugin/PDFDocumentPage.qml index 8abd595b..63f4541d 100644 --- a/plugin/PDFDocumentPage.qml +++ b/plugin/PDFDocumentPage.qml @@ -218,6 +218,7 @@ DocumentPage { id: row property bool active: pageCount.highlighted || linkBack.visible + || rotateButton.highlighted || search.highlighted || !search.iconized || textButton.highlighted @@ -390,6 +391,22 @@ DocumentPage { toolbar.hide() } } + BackgroundItem { + id: rotateTool + + width: row.itemWidth + height: parent.height + highlighted: pressed || rotateButton.pressed + onClicked: view.rotate() + IconButton { + id: rotateButton + anchors.centerIn: parent + highlighted: pressed || row.activeItem === rotateTool + icon.source: row.activeItem === rotateTool ? "image://theme/icon-m-rotate-right-selected" + : "image://theme/icon-m-rotate-right" + onClicked: view.rotate() + } + } BackgroundItem { id: pageCount width: screen.sizeCategory <= Screen.Medium diff --git a/plugin/PDFView.qml b/plugin/PDFView.qml index 4636a4e9..5d1b763c 100644 --- a/plugin/PDFView.qml +++ b/plugin/PDFView.qml @@ -60,6 +60,10 @@ SilicaFlickable { return Math.max(width, Math.min(value, maximumZoom)) } + function rotate() { + pdfCanvas.rotate(true) + } + function zoom(amount, center) { var oldWidth = pdfCanvas.width var oldHeight = pdfCanvas.height From 95087f8e255a6d10dfedb9a1803d6705625ba3ff Mon Sep 17 00:00:00 2001 From: Damien Caliste Date: Thu, 15 Mar 2018 21:19:20 +0100 Subject: [PATCH 3/4] [sailfish-office] Store user orientation as a setting per document. --- plugin/PDFDocumentPage.qml | 3 ++- plugin/PDFStorage.js | 29 ++++++++++++++++++++--------- plugin/PDFView.qml | 1 + 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/plugin/PDFDocumentPage.qml b/plugin/PDFDocumentPage.qml index 63f4541d..7459b323 100644 --- a/plugin/PDFDocumentPage.qml +++ b/plugin/PDFDocumentPage.qml @@ -47,7 +47,7 @@ DocumentPage { _settings = new PDFStorage.Settings(pdfDocument.source) } var last = view.getPagePosition() - _settings.setLastPage(last[0] + 1, last[1], last[2], view.itemWidth) + _settings.setLastPage(last[0] + 1, last[1], last[2], view.itemWidth, view.pageRotation) } attachedPage: Component { @@ -73,6 +73,7 @@ DocumentPage { _settings = new PDFStorage.Settings(pdfDocument.source) } var last = _settings.getLastPage() + view.pageRotation = last[4] if (last[3] > 0) { view.itemWidth = last[3] view.adjust() diff --git a/plugin/PDFStorage.js b/plugin/PDFStorage.js index a02d9383..5667312f 100644 --- a/plugin/PDFStorage.js +++ b/plugin/PDFStorage.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Caliste Damien. + * Copyright (C) 2015-2018 Caliste Damien. * Contact: Damien Caliste * * This program is free software; you can redistribute it and/or @@ -24,15 +24,24 @@ var Settings = function(file) { /* Different tables. */ function createTableLastViewSettings(tx) { - /* Currently store the last page, may be altered later to store - zoom level or page position. */ + tx.executeSql("CREATE TABLE IF NOT EXISTS Version(" + + "id INT)") + var rs = tx.executeSql('SELECT id FROM Version') + var id = 0 + if (rs.rows.length > 0) id = rs.rows.item(0).id + tx.executeSql("CREATE TABLE IF NOT EXISTS LastViewSettings(" + "file TEXT NOT NULL," + "page INT NOT NULL," + "top REAL ," + "left REAL ," - + "width INT CHECK(width > 0))"); - tx.executeSql('CREATE UNIQUE INDEX IF NOT EXISTS idx_file ON LastViewSettings(file)'); + + "width INT CHECK(width > 0)," + + "orientation INT DEFAULT 0)") + tx.executeSql('CREATE UNIQUE INDEX IF NOT EXISTS idx_file ON LastViewSettings(file)') + if (id == 0) + tx.executeSql('ALTER TABLE LastViewSettings ADD COLUMN orientation INT DEFAULT 0') + + tx.executeSql('INSERT OR REPLACE INTO Version(id) VALUES (1)') } /* Get and set operations. */ @@ -42,24 +51,26 @@ Settings.prototype.getLastPage = function() { var left = 0 var width = 0 var file = this.source + var orientation = 0 this.db.transaction(function(tx) { createTableLastViewSettings(tx); - var rs = tx.executeSql('SELECT page, top, left, width FROM LastViewSettings WHERE file = ?', [file]); + var rs = tx.executeSql('SELECT page, top, left, width, orientation FROM LastViewSettings WHERE file = ?', [file]); if (rs.rows.length > 0) { page = rs.rows.item(0).page; top = rs.rows.item(0).top; left = rs.rows.item(0).left; width = rs.rows.item(0).width; + orientation = rs.rows.item(0).orientation; } }); // Return page is in [1:] - return [page, top, left, width]; + return [page, top, left, width, orientation]; } -Settings.prototype.setLastPage = function(page, top, left, width) { +Settings.prototype.setLastPage = function(page, top, left, width, orientation) { // page is in [1:] var file = this.source this.db.transaction(function(tx) { createTableLastViewSettings(tx); - var rs = tx.executeSql('INSERT OR REPLACE INTO LastViewSettings(file, page, top, left, width) VALUES (?,?,?,?,?)', [file, page, top, left, width]); + var rs = tx.executeSql('INSERT OR REPLACE INTO LastViewSettings(file, page, top, left, width, orientation) VALUES (?,?,?,?,?,?)', [file, page, top, left, width, orientation]); }); } diff --git a/plugin/PDFView.qml b/plugin/PDFView.qml index 5d1b763c..3e4fdda5 100644 --- a/plugin/PDFView.qml +++ b/plugin/PDFView.qml @@ -30,6 +30,7 @@ SilicaFlickable { property alias itemWidth: pdfCanvas.width property alias itemHeight: pdfCanvas.height property alias document: pdfCanvas.document + property alias pageRotation: pdfCanvas.pageRotation property int currentPage: !quickScrollAnimation.running ? pdfCanvas.currentPage : quickScrollAnimation.pageTo property alias selection: pdfSelection From 6413916de5dd19aee6c076668b053aafba0270fb Mon Sep 17 00:00:00 2001 From: Damien Caliste Date: Mon, 19 Mar 2018 10:35:21 +0100 Subject: [PATCH 4/4] [sailfish-office] Implement the document rotation action as a push up menu attached to the toolbar. --- plugin/PDFDocumentPage.qml | 377 ++++++++++++++++++------------------- 1 file changed, 188 insertions(+), 189 deletions(-) diff --git a/plugin/PDFDocumentPage.qml b/plugin/PDFDocumentPage.qml index 7459b323..1efc562d 100644 --- a/plugin/PDFDocumentPage.qml +++ b/plugin/PDFDocumentPage.qml @@ -106,6 +106,8 @@ DocumentPage { anchors.fill: parent anchors.bottomMargin: toolbar.offset + opacity: toolbarMenu.active ? 0.1 : 1. + Behavior on opacity {FadeAnimation{}} document: pdfDocument onCanMoveBackChanged: if (canMoveBack) toolbar.show() onClicked: base.open = !base.open @@ -183,7 +185,7 @@ DocumentPage { || (contextMenuLinks && contextMenuLinks.active) || (contextMenuHighlight && contextMenuHighlight.active) || (contextMenuText && contextMenuText.active) - autoShowHide: !row.active + autoShowHide: !row.active && !toolbarMenu.active function noticeShow(message) { if (!notice) { @@ -215,216 +217,213 @@ DocumentPage { } // Toolbar contain. - Row { - id: row - property bool active: pageCount.highlighted - || linkBack.visible - || rotateButton.highlighted - || search.highlighted - || !search.iconized - || textButton.highlighted - || highlightButton.highlighted - || view.selection.selected - property Item activeItem - property int nVisibleChildren: children.length - (linkBack.visible ? 0 : 1) - property real itemWidth: Math.max(toolbar.width - pageCount.width, 0) - / (nVisibleChildren - 1) - height: parent.height - - function toggle(item) { - if (toolbar.notice) toolbar.notice.hide() - view.selection.unselect() - if (row.activeItem === item) { - row.activeItem = null - } else { - row.activeItem = item + SilicaFlickable { + width: parent.width + height: toolbar.height + contentHeight: row.height + + PushUpMenu { + id: toolbarMenu + MenuItem { + //% "Rotate document" + text: qsTrId("sailfish-office-me-rotate_document"); + onClicked: view.rotate() } } - - SearchBarItem { - id: search - width: toolbar.width - iconizedWidth: row.itemWidth - height: parent.height - - searching: pdfDocument.searching - searchProgress: pdfDocument.searchModel ? pdfDocument.searchModel.fraction : 0. - matchCount: pdfDocument.searchModel ? pdfDocument.searchModel.count : -1 - - onRequestSearch: pdfDocument.search(text, view.currentPage - 1) - onRequestPreviousMatch: view.prevSearchMatch() - onRequestNextMatch: view.nextSearchMatch() - onRequestCancel: pdfDocument.cancelSearch(!pdfDocument.searching) - onClicked: row.toggle(search) - } - BackgroundItem { - id: textTool - property bool first: true - - width: row.itemWidth - height: parent.height - highlighted: pressed || textButton.pressed - onClicked: { - row.toggle(textTool) - if (textTool.first) { - //% "Tap where you want to add a note" - toolbar.noticeShow(qsTrId("sailfish-office-la-notice-anno-text")) - textTool.first = false + Row { + id: row + property bool active: pageCount.highlighted + || linkBack.visible + || search.highlighted + || !search.iconized + || textButton.highlighted + || highlightButton.highlighted + || view.selection.selected + property Item activeItem + property int nVisibleChildren: children.length - (linkBack.visible ? 0 : 1) + property real itemWidth: Math.max(toolbar.width - pageCount.width, 0) + / (nVisibleChildren - 1) + height: toolbar.height + + function toggle(item) { + if (toolbar.notice) toolbar.notice.hide() + view.selection.unselect() + if (row.activeItem === item) { + row.activeItem = null + } else { + row.activeItem = item } } - IconButton { - id: textButton - anchors.centerIn: parent - highlighted: pressed || textTool.pressed || row.activeItem === textTool - icon.source: row.activeItem === textTool ? "image://theme/icon-m-annotation-selected" - : "image://theme/icon-m-annotation" - onClicked: textTool.clicked(mouse) + + SearchBarItem { + id: search + width: toolbar.width + iconizedWidth: row.itemWidth + height: parent.height + + searching: pdfDocument.searching + searchProgress: pdfDocument.searchModel ? pdfDocument.searchModel.fraction : 0. + matchCount: pdfDocument.searchModel ? pdfDocument.searchModel.count : -1 + + onRequestSearch: pdfDocument.search(text, view.currentPage - 1) + onRequestPreviousMatch: view.prevSearchMatch() + onRequestNextMatch: view.nextSearchMatch() + onRequestCancel: pdfDocument.cancelSearch(!pdfDocument.searching) + onClicked: row.toggle(search) } - MouseArea { - parent: row.activeItem === textTool ? view : null - anchors.fill: parent + BackgroundItem { + id: textTool + property bool first: true + + width: row.itemWidth + height: parent.height + highlighted: pressed || textButton.pressed onClicked: { - var annotation = textComponent.createObject(textTool) - var pt = Qt.point(view.contentX + mouse.x, - view.contentY + mouse.y) - pdfDocument.create(annotation, - function() { - var at = view.getPositionAt(pt) - annotation.attachAt(pdfDocument, - at[0], at[2], at[1]) - }) row.toggle(textTool) + if (textTool.first) { + //% "Tap where you want to add a note" + toolbar.noticeShow(qsTrId("sailfish-office-la-notice-anno-text")) + textTool.first = false + } } - Component { - id: textComponent - PDF.TextAnnotation { } + IconButton { + id: textButton + anchors.centerIn: parent + highlighted: pressed || textTool.pressed || row.activeItem === textTool + icon.source: row.activeItem === textTool ? "image://theme/icon-m-annotation-selected" + : "image://theme/icon-m-annotation" + onClicked: textTool.clicked(mouse) + } + MouseArea { + parent: row.activeItem === textTool ? view : null + anchors.fill: parent + onClicked: { + var annotation = textComponent.createObject(textTool) + var pt = Qt.point(view.contentX + mouse.x, + view.contentY + mouse.y) + pdfDocument.create(annotation, + function() { + var at = view.getPositionAt(pt) + annotation.attachAt(pdfDocument, + at[0], at[2], at[1]) + }) + row.toggle(textTool) + } + Component { + id: textComponent + PDF.TextAnnotation { } + } } } - } - BackgroundItem { - id: highlightTool - property bool first: true - - function highlightSelection() { - var anno = highlightComponent.createObject(highlightTool) - anno.color = highlightColorConfig.value - anno.style = highlightStyleConfig.toEnum(highlightStyleConfig.value) - anno.attach(pdfDocument, view.selection) - toolbar.hide() - } - - width: row.itemWidth - height: parent.height - highlighted: pressed || highlightButton.pressed - onClicked: { - if (view.selection.selected) { - highlightSelection() - view.selection.unselect() - return + BackgroundItem { + id: highlightTool + property bool first: true + + function highlightSelection() { + var anno = highlightComponent.createObject(highlightTool) + anno.color = highlightColorConfig.value + anno.style = highlightStyleConfig.toEnum(highlightStyleConfig.value) + anno.attach(pdfDocument, view.selection) + toolbar.hide() } - row.toggle(highlightTool) - if (highlightTool.first) { - //% "Tap and move your finger over the area" - toolbar.noticeShow(qsTrId("sailfish-office-la-notice-anno-highlight")) - highlightTool.first = false + + width: row.itemWidth + height: parent.height + highlighted: pressed || highlightButton.pressed + onClicked: { + if (view.selection.selected) { + highlightSelection() + view.selection.unselect() + return + } + row.toggle(highlightTool) + if (highlightTool.first) { + //% "Tap and move your finger over the area" + toolbar.noticeShow(qsTrId("sailfish-office-la-notice-anno-highlight")) + highlightTool.first = false + } } - } - Component { - id: highlightComponent - PDF.HighlightAnnotation { } - } + Component { + id: highlightComponent + PDF.HighlightAnnotation { } + } - IconButton { - id: highlightButton - anchors.centerIn: parent - highlighted: pressed || highlightTool.pressed || row.activeItem === highlightTool - icon.source: row.activeItem === highlightTool ? "image://theme/icon-m-edit-selected" - : "image://theme/icon-m-edit" - onClicked: highlightTool.clicked(mouse) - } - MouseArea { - parent: row.activeItem === highlightTool ? view : null - anchors.fill: parent - preventStealing: true - onPressed: { - view.selection.selectAt(Qt.point(view.contentX + mouse.x, - view.contentY + mouse.y)) + IconButton { + id: highlightButton + anchors.centerIn: parent + highlighted: pressed || highlightTool.pressed || row.activeItem === highlightTool + icon.source: row.activeItem === highlightTool ? "image://theme/icon-m-edit-selected" + : "image://theme/icon-m-edit" + onClicked: highlightTool.clicked(mouse) } - onPositionChanged: { - if (view.selection.count < 1) { + MouseArea { + parent: row.activeItem === highlightTool ? view : null + anchors.fill: parent + preventStealing: true + onPressed: { view.selection.selectAt(Qt.point(view.contentX + mouse.x, view.contentY + mouse.y)) - } else { - view.selection.handle2 = Qt.point(view.contentX + mouse.x, - view.contentY + mouse.y) + } + onPositionChanged: { + if (view.selection.count < 1) { + view.selection.selectAt(Qt.point(view.contentX + mouse.x, + view.contentY + mouse.y)) + } else { + view.selection.handle2 = Qt.point(view.contentX + mouse.x, + view.contentY + mouse.y) + } + } + onReleased: { + if (view.selection.selected) highlightTool.highlightSelection() + row.toggle(highlightTool) + } + Binding { + target: view + property: "selectionDraggable" + value: row.activeItem !== highlightTool } } - onReleased: { - if (view.selection.selected) highlightTool.highlightSelection() - row.toggle(highlightTool) + } + BackgroundItem { + id: linkBack + width: row.itemWidth + height: parent.height + highlighted: pressed || backButton.pressed + opacity: view.canMoveBack ? 1. : 0. + visible: opacity > 0 + Behavior on opacity { FadeAnimation{ duration: 400 } } + IconButton { + id: backButton + anchors.centerIn: parent + highlighted: pressed || linkBack.pressed + icon.source: "image://theme/icon-m-back" + onClicked: linkBack.clicked(mouse) } - Binding { - target: view - property: "selectionDraggable" - value: row.activeItem !== highlightTool + onClicked: { + row.toggle(linkBack) + view.moveBack() + toolbar.hide() } } - } - BackgroundItem { - id: linkBack - width: row.itemWidth - height: parent.height - highlighted: pressed || backButton.pressed - opacity: view.canMoveBack ? 1. : 0. - visible: opacity > 0 - Behavior on opacity { FadeAnimation{ duration: 400 } } - IconButton { - id: backButton - anchors.centerIn: parent - highlighted: pressed || linkBack.pressed - icon.source: "image://theme/icon-m-back" - onClicked: linkBack.clicked(mouse) - } - onClicked: { - row.toggle(linkBack) - view.moveBack() - toolbar.hide() - } - } - BackgroundItem { - id: rotateTool - - width: row.itemWidth - height: parent.height - highlighted: pressed || rotateButton.pressed - onClicked: view.rotate() - IconButton { - id: rotateButton - anchors.centerIn: parent - highlighted: pressed || row.activeItem === rotateTool - icon.source: row.activeItem === rotateTool ? "image://theme/icon-m-rotate-right-selected" - : "image://theme/icon-m-rotate-right" - onClicked: view.rotate() - } - } - BackgroundItem { - id: pageCount - width: screen.sizeCategory <= Screen.Medium - ? Math.max(toolbar.width / row.nVisibleChildren, Screen.width / 4) - : toolbar.width / row.nVisibleChildren - height: parent.height - Label { - id: pageLabel - anchors.centerIn: parent - width: Math.min(parent.width - Theme.paddingSmall, implicitWidth) - fontSizeMode: Text.HorizontalFit - color: pageCount.highlighted ? Theme.highlightColor : Theme.primaryColor - text: view.currentPage + " | " + view.document.pageCount - } - onClicked: { - row.toggle(pageCount) - base.pushAttachedPage() + BackgroundItem { + id: pageCount + width: screen.sizeCategory <= Screen.Medium + ? Math.max(toolbar.width / row.nVisibleChildren, Screen.width / 4) + : toolbar.width / row.nVisibleChildren + height: parent.height + Label { + id: pageLabel + anchors.centerIn: parent + width: Math.min(parent.width - Theme.paddingSmall, implicitWidth) + fontSizeMode: Text.HorizontalFit + color: pageCount.highlighted ? Theme.highlightColor : Theme.primaryColor + text: view.currentPage + " | " + view.document.pageCount + } + onClicked: { + row.toggle(pageCount) + base.pushAttachedPage() + } } } }