Skip to content

Commit

Permalink
[office] Add a search engine to PDFDocument objects.
Browse files Browse the repository at this point in the history
 The search results are exposed in QML via a customed QAbstractListModel.
  • Loading branch information
dcaliste committed May 28, 2015
1 parent 5a52ab2 commit dbfe4d9
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 7 deletions.
1 change: 1 addition & 0 deletions pdf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(pdfplugin_SRCS
pdftocmodel.cpp
pdfcanvas.cpp
pdflinkarea.cpp
pdfsearchmodel.cpp
)

add_library(sailfishofficepdfplugin MODULE ${pdfplugin_SRCS})
Expand Down
24 changes: 19 additions & 5 deletions pdf/pdfcanvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,6 @@ void PDFCanvas::layout()

PDFDocument::LinkMap links = d->document->linkTargets();

float scale = width() / d->renderWidth;

float totalHeight = 0.f;
for( int i = 0; i < d->pageCount; ++i )
{
Expand All @@ -226,7 +224,7 @@ void PDFCanvas::layout()

PDFPage page;
page.index = i;
page.rect = QRectF(0, totalHeight, d->renderWidth * scale, d->renderWidth * ratio * scale);
page.rect = QRectF(0, totalHeight, width(), width() * ratio);
page.links = links.values( i );
page.requested = false; // We're cancelling all requests below
if (d->pages.contains(i)) {
Expand All @@ -245,6 +243,8 @@ void PDFCanvas::layout()
// pending reuqests to minimize the delay before they come.
d->document->cancelPageRequest(-1);

emit pageLayoutChanged();

update();
}

Expand Down Expand Up @@ -273,6 +273,18 @@ QUrl PDFCanvas::urlAtPoint(const QPoint& point)
return QUrl();
}

QRectF PDFCanvas::fromPageToItem(int index, const QRectF &rect)
{
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());
}

void PDFCanvas::pageFinished( int id, QSGTexture *texture )
{
PDFPage& page = d->pages[ id ];
Expand All @@ -287,8 +299,10 @@ void PDFCanvas::pageFinished( int id, QSGTexture *texture )

void PDFCanvas::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
{
QMetaObject::invokeMethod(d->resizeTimer, "start");
layout();
if (oldGeometry.width() != newGeometry.width()) {
QMetaObject::invokeMethod(d->resizeTimer, "start");
layout();
}
QQuickItem::geometryChanged(newGeometry, oldGeometry);
}

Expand Down
6 changes: 6 additions & 0 deletions pdf/pdfcanvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,19 @@ class PDFCanvas : public QQuickItem
* \return The url of the link at point or an empty url if there is no link at point.
*/
QUrl urlAtPoint( const QPoint& point );
/**
* \return A rectangle in the canvas coordinates from a rectangle
* in page coordinates. Index is the index of the page.
*/
Q_INVOKABLE QRectF fromPageToItem(int index, const QRectF &rect);

Q_SIGNALS:
void documentChanged();
void flickableChanged();
void spacingChanged();
void linkColorChanged();
void currentPageChanged();
void pageLayoutChanged();

protected:
virtual void geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry);
Expand Down
49 changes: 48 additions & 1 deletion pdf/pdfdocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "pdfdocument.h"
#include "pdfrenderthread.h"
#include "pdfjob.h"
#include "pdfsearchmodel.h"

#include <QDebug>
#include <QUrl>
Expand All @@ -28,10 +29,13 @@
class PDFDocument::Private
{
public:
Private() : document( nullptr ), completed(false) { }
Private() : searching(false), document( nullptr ), completed(false) { }

PDFRenderThread* thread;

bool searching;
PDFSearchModel *searchModel;

Poppler::Document* document;
QString source;
bool completed;
Expand All @@ -45,11 +49,15 @@ PDFDocument::PDFDocument(QObject* parent)
connect( d->thread, &PDFRenderThread::loadFinished, this, &PDFDocument::pageCountChanged );
connect( d->thread, &PDFRenderThread::loadFinished, this, &PDFDocument::loadFinished );
connect( d->thread, &PDFRenderThread::jobFinished, this, &PDFDocument::jobFinished );

d->searchModel = nullptr;
}

PDFDocument::~PDFDocument()
{
delete d->thread;
if (d->searchModel != nullptr)
delete d->searchModel;
}

QString PDFDocument::source() const
Expand All @@ -72,6 +80,16 @@ QObject* PDFDocument::tocModel() const
return d->thread->tocModel();
}

QObject* PDFDocument::searchModel() const
{
return d->searchModel;
}

bool PDFDocument::searching() const
{
return d->searching;
}

bool PDFDocument::isLoaded() const
{
return d->thread->isLoaded();
Expand Down Expand Up @@ -166,6 +184,25 @@ void PDFDocument::requestPageSizes()
d->thread->queueJob( job );
}

void PDFDocument::search(const QString &search, uint startPage)
{
if(!isLoaded() || isLocked())
return;

if (d->searchModel != nullptr) {
delete d->searchModel;
d->searchModel = nullptr;
emit searchModelChanged();
}

if (search.length() > 0) {
d->searching = true;
emit searchingChanged();
SearchDocumentJob* job = new SearchDocumentJob(search, startPage);
d->thread->queueJob(job);
}
}

void PDFDocument::loadFinished()
{
if (d->thread->isFailed())
Expand All @@ -191,6 +228,16 @@ void PDFDocument::jobFinished(PDFJob* job)
emit pageSizesFinished(j->m_pageSizes);
break;
}
case PDFJob::SearchDocumentJob: {
SearchDocumentJob* j = static_cast<SearchDocumentJob*>(job);
if (d->searchModel)
delete d->searchModel;
d->searchModel = new PDFSearchModel(j->m_matches);
emit searchModelChanged();
d->searching = false;
emit searchingChanged();
break;
}
default:
break;
}
Expand Down
9 changes: 8 additions & 1 deletion pdf/pdfdocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class PDFDocument : public QObject, public QQmlParserStatus
Q_PROPERTY(bool loaded READ isLoaded NOTIFY documentLoaded)
Q_PROPERTY(bool failure READ isFailed NOTIFY documentFailed)
Q_PROPERTY(bool locked READ isLocked NOTIFY documentLocked)
Q_PROPERTY(bool searching READ searching NOTIFY searchingChanged)
Q_PROPERTY(QObject* searchModel READ searchModel NOTIFY searchModelChanged)

Q_INTERFACES(QQmlParserStatus)

Expand All @@ -55,7 +57,9 @@ class PDFDocument : public QObject, public QQmlParserStatus
QString source() const;
int pageCount() const;
QObject* tocModel() const;

bool searching() const;
QObject* searchModel() const;

LinkMap linkTargets() const;

bool isLoaded() const;
Expand All @@ -72,13 +76,16 @@ public Q_SLOTS:
void prioritizeRequest( int index, int size);
void cancelPageRequest(int index);
void requestPageSizes();
void search(const QString& search, uint startPage = 0);
void loadFinished();
void jobFinished(PDFJob* job);

Q_SIGNALS:
void sourceChanged();
void pageCountChanged();
void tocModelChanged();
void searchingChanged();
void searchModelChanged();

void documentLoaded();
void documentFailed();
Expand Down
36 changes: 36 additions & 0 deletions pdf/pdfjob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,39 @@ void PageSizesJob::run()
m_pageSizes.append( page->pageSizeF() );
}
}

SearchDocumentJob::SearchDocumentJob(const QString& search, uint page)
: PDFJob(PDFJob::SearchDocumentJob), m_search(search), startPage(page)
{

}

void SearchDocumentJob::run()
{
Q_ASSERT(m_document);

for( int i = 0; i < m_document->numPages(); ++i )
{
int ipage = (startPage + i) % m_document->numPages();
Poppler::Page* page = m_document->page(ipage);

double sLeft, sTop, sRight, sBottom;
float scaleW = 1.f / page->pageSizeF().width();
float scaleH = 1.f / page->pageSizeF().height();
bool found;
found = page->search(m_search, sLeft, sTop, sRight, sBottom,
Poppler::Page::FromTop,
Poppler::Page::CaseInsensitive);
while (found) {
QRectF result;
result.setLeft(sLeft * scaleW);
result.setTop(sTop * scaleH);
result.setRight(sRight * scaleW);
result.setBottom(sBottom * scaleH);
m_matches.append(QPair<int, QRectF>(ipage, result));
found = page->search(m_search, sLeft, sTop, sRight, sBottom,
Poppler::Page::NextResult,
Poppler::Page::CaseInsensitive);
}
}
}
17 changes: 17 additions & 0 deletions pdf/pdfjob.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class PDFJob : public QObject
UnLockDocumentJob,
RenderPageJob,
PageSizesJob,
SearchDocumentJob,
};

PDFJob( JobType type ) : m_document{ nullptr }, m_type{ type } { }
Expand Down Expand Up @@ -108,4 +109,20 @@ class PageSizesJob : public PDFJob
QList< QSizeF > m_pageSizes;
};

class SearchDocumentJob : public PDFJob
{
Q_OBJECT
public:
SearchDocumentJob( const QString& search, uint page );

virtual void run();

QList<QPair<int, QRectF>> m_matches;

private:
QString m_search;
uint startPage;
};


#endif // PDFJOB_H
76 changes: 76 additions & 0 deletions pdf/pdfsearchmodel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (C) 2015 Caliste Damien.
* Contact: Damien Caliste <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2 only.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include "pdfsearchmodel.h"

#include <QRectF>

PDFSearchModel::PDFSearchModel(const QList< QPair<int, QRectF> > &matches, QObject* parent)
: QAbstractListModel(parent), m_matches(matches)
{
}

PDFSearchModel::~PDFSearchModel()
{
}

QHash< int, QByteArray > PDFSearchModel::roleNames() const
{
QHash< int, QByteArray > names;
names[Page] = "page";
names[Rect] = "rect";
return names;
}

QVariant PDFSearchModel::data(const QModelIndex& index, int role) const
{
QVariant result;
if(index.isValid())
{
int row = index.row();
if(row > -1 && row < m_matches.count())
{
const QPair<int, QRectF> &match = m_matches.at(row);
switch(role)
{
case Page:
result.setValue<int>(match.first);
break;
case Rect:
result.setValue<QRectF>(match.second);
break;
default:
result.setValue<QString>(QString("Unknown role: %1").arg(role));
break;
}
}
}
return result;
}

int PDFSearchModel::rowCount(const QModelIndex& parent) const
{
if(parent.isValid())
return 0;
return m_matches.count();
}

int PDFSearchModel::count() const
{
return m_matches.count();
}
Loading

0 comments on commit dbfe4d9

Please sign in to comment.