diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2020-02-25 17:49:56 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2020-02-26 15:40:24 +0100 |
commit | 926afcaa6f3ce02b51cd535d82454f4b25e0b7f6 (patch) | |
tree | 66773defe3e53fc4a37f3fc10eb2f2649d27f668 | |
parent | 8091944ae8ccfb4e0b3e7518912802c9c75704d0 (diff) |
PdfSearchModel: provide ContextBefore and ContextAfter
...as separate roles, to make alignment easier, and to avoid hard-coding
HTML tags in the Context role as it was before.
But the strings in these context roles are not always adjacent to the
search results in geometric coordinates sometimes, in some PDF files,
despite having adjacent character indices. I.e. the "next" character
after the search string, or the "previous" character before it, could be
anywhere on the page.
Change-Id: Ief0a490b64fdb3c3ca98506926650648b609ece1
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
-rw-r--r-- | examples/pdf/multipage/viewer.qml | 26 | ||||
-rw-r--r-- | examples/pdf/pdfviewer/viewer.qml | 26 | ||||
-rw-r--r-- | src/pdf/api/qpdfsearchmodel.h | 3 | ||||
-rw-r--r-- | src/pdf/api/qpdfsearchresult.h | 8 | ||||
-rw-r--r-- | src/pdf/api/qpdfsearchresult_p.h | 8 | ||||
-rw-r--r-- | src/pdf/qpdfsearchmodel.cpp | 42 | ||||
-rw-r--r-- | src/pdf/qpdfsearchresult.cpp | 16 |
7 files changed, 101 insertions, 28 deletions
diff --git a/examples/pdf/multipage/viewer.qml b/examples/pdf/multipage/viewer.qml index 9e5f92407..282636564 100644 --- a/examples/pdf/multipage/viewer.qml +++ b/examples/pdf/multipage/viewer.qml @@ -258,7 +258,31 @@ ApplicationWindow { ScrollBar.vertical: ScrollBar { } delegate: ItemDelegate { width: parent ? parent.width : 0 - text: "page " + (page + 1) + ": " + context + RowLayout { + anchors.fill: parent + spacing: 0 + Label { + text: "Page " + (page + 1) + ": " + } + Label { + text: contextBefore + elide: Text.ElideLeft + horizontalAlignment: Text.AlignRight + Layout.fillWidth: true + Layout.preferredWidth: parent.width / 2 + } + Label { + font.bold: true + text: view.searchString + width: implicitWidth + } + Label { + text: contextAfter + elide: Text.ElideRight + Layout.fillWidth: true + Layout.preferredWidth: parent.width / 2 + } + } highlighted: ListView.isCurrentItem onClicked: { searchResultsList.currentIndex = index diff --git a/examples/pdf/pdfviewer/viewer.qml b/examples/pdf/pdfviewer/viewer.qml index e3bb4b474..b47a7dd36 100644 --- a/examples/pdf/pdfviewer/viewer.qml +++ b/examples/pdf/pdfviewer/viewer.qml @@ -231,7 +231,31 @@ ApplicationWindow { ScrollBar.vertical: ScrollBar { } delegate: ItemDelegate { width: parent ? parent.width : 0 - text: "page " + (page + 1) + ": " + context + RowLayout { + anchors.fill: parent + spacing: 0 + Label { + text: "Page " + (page + 1) + ": " + } + Label { + text: contextBefore + elide: Text.ElideLeft + horizontalAlignment: Text.AlignRight + Layout.fillWidth: true + Layout.preferredWidth: parent.width / 2 + } + Label { + font.bold: true + text: view.searchString + width: implicitWidth + } + Label { + text: contextAfter + elide: Text.ElideRight + Layout.fillWidth: true + Layout.preferredWidth: parent.width / 2 + } + } highlighted: ListView.isCurrentItem onClicked: { searchResultsList.currentIndex = index diff --git a/src/pdf/api/qpdfsearchmodel.h b/src/pdf/api/qpdfsearchmodel.h index cc91e214a..f1593b64b 100644 --- a/src/pdf/api/qpdfsearchmodel.h +++ b/src/pdf/api/qpdfsearchmodel.h @@ -58,7 +58,8 @@ public: Page = Qt::UserRole, IndexOnPage, Location, - Context, + ContextBefore, + ContextAfter, _Count }; Q_ENUM(Role) diff --git a/src/pdf/api/qpdfsearchresult.h b/src/pdf/api/qpdfsearchresult.h index db7af3dd9..84a2f2343 100644 --- a/src/pdf/api/qpdfsearchresult.h +++ b/src/pdf/api/qpdfsearchresult.h @@ -50,18 +50,20 @@ class QPdfSearchResultPrivate; class Q_PDF_EXPORT QPdfSearchResult : public QPdfDestination { Q_GADGET - Q_PROPERTY(QString context READ context) + Q_PROPERTY(QString contextBefore READ contextBefore) + Q_PROPERTY(QString contextAfter READ contextAfter) Q_PROPERTY(QVector<QRectF> rectangles READ rectangles) public: QPdfSearchResult(); ~QPdfSearchResult() {} - QString context() const; + QString contextBefore() const; + QString contextAfter() const; QVector<QRectF> rectangles() const; private: - QPdfSearchResult(int page, QVector<QRectF> rects, QString context); + QPdfSearchResult(int page, QVector<QRectF> rects, QString contextBefore, QString contextAfter); QPdfSearchResult(QPdfSearchResultPrivate *d); friend class QPdfDocument; friend class QPdfSearchModelPrivate; diff --git a/src/pdf/api/qpdfsearchresult_p.h b/src/pdf/api/qpdfsearchresult_p.h index a0f8e4457..615dce4e0 100644 --- a/src/pdf/api/qpdfsearchresult_p.h +++ b/src/pdf/api/qpdfsearchresult_p.h @@ -56,12 +56,14 @@ class QPdfSearchResultPrivate : public QPdfDestinationPrivate { public: QPdfSearchResultPrivate() = default; - QPdfSearchResultPrivate(int page, QVector<QRectF> rects, QString context) : + QPdfSearchResultPrivate(int page, QVector<QRectF> rects, QString contextBefore, QString contextAfter) : QPdfDestinationPrivate(page, rects.first().topLeft(), 0), - context(context), + contextBefore(contextBefore), + contextAfter(contextAfter), rects(rects) {} - QString context; + QString contextBefore; + QString contextAfter; QVector<QRectF> rects; }; diff --git a/src/pdf/qpdfsearchmodel.cpp b/src/pdf/qpdfsearchmodel.cpp index 4129c7cb7..27b7833fc 100644 --- a/src/pdf/qpdfsearchmodel.cpp +++ b/src/pdf/qpdfsearchmodel.cpp @@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(qLcS, "qt.pdf.search") static const int UpdateTimerInterval = 100; -static const int ContextChars = 20; +static const int ContextChars = 64; static const double CharacterHitTolerance = 6.0; QPdfSearchModel::QPdfSearchModel(QObject *parent) @@ -95,13 +95,19 @@ QVariant QPdfSearchModel::data(const QModelIndex &index, int role) const return pi.index; case Role::Location: return d->searchResults[pi.page][pi.index].location(); - case Role::Context: - return d->searchResults[pi.page][pi.index].context(); + case Role::ContextBefore: + return d->searchResults[pi.page][pi.index].contextBefore(); + case Role::ContextAfter: + return d->searchResults[pi.page][pi.index].contextAfter(); case Role::_Count: break; } - if (role == Qt::DisplayRole) - return d->searchResults[pi.page][pi.index].context(); + if (role == Qt::DisplayRole) { + const QString ret = d->searchResults[pi.page][pi.index].contextBefore() + + QLatin1String("<b>") + d->searchString + QLatin1String("</b>") + + d->searchResults[pi.page][pi.index].contextAfter(); + return ret; + } return QVariant(); } @@ -124,9 +130,9 @@ void QPdfSearchModel::setSearchString(QString searchString) return; d->searchString = searchString; - emit searchStringChanged(); beginResetModel(); d->clearResults(); + emit searchStringChanged(); endResetModel(); } @@ -161,8 +167,8 @@ void QPdfSearchModel::setDocument(QPdfDocument *document) return; d->document = document; - emit documentChanged(); d->clearResults(); + emit documentChanged(); } void QPdfSearchModel::timerEvent(QTimerEvent *event) @@ -179,7 +185,7 @@ void QPdfSearchModel::timerEvent(QTimerEvent *event) d->doSearch(d->nextPageToUpdate++); } -QPdfSearchModelPrivate::QPdfSearchModelPrivate() +QPdfSearchModelPrivate::QPdfSearchModelPrivate() : QAbstractItemModelPrivate() { } @@ -246,7 +252,7 @@ bool QPdfSearchModelPrivate::doSearch(int page) } qCDebug(qLcS) << rects.last() << "char idx" << startIndex << "->" << endIndex; } - QString context; + QString contextBefore, contextAfter; if (startIndex >= 0 || endIndex >= 0) { startIndex = qMax(0, startIndex - ContextChars); endIndex += ContextChars; @@ -255,13 +261,21 @@ bool QPdfSearchModelPrivate::doSearch(int page) QVector<ushort> buf(count + 1); int len = FPDFText_GetText(textPage, startIndex, count, buf.data()); Q_ASSERT(len - 1 <= count); // len is number of characters written, including the terminator - context = QString::fromUtf16(buf.constData(), len - 1); - context = context.replace(QLatin1Char('\n'), QLatin1Char(' ')); - context = context.replace(searchString, - QLatin1String("<b>") + searchString + QLatin1String("</b>")); + QString context = QString::fromUtf16(buf.constData(), len - 1); + context = context.replace(QLatin1Char('\n'), QStringLiteral("\u23CE")); + context = context.remove(QLatin1Char('\r')); + // try to find the search string near the middle of the context if possible + int si = context.indexOf(searchString, ContextChars - 5, Qt::CaseInsensitive); + if (si < 0) + si = context.indexOf(searchString, Qt::CaseInsensitive); + if (si < 0) + qWarning() << "search string" << searchString << "not found in context" << context; + contextBefore = context.mid(0, si); + contextAfter = context.mid(si + searchString.length()); } } - newSearchResults << QPdfSearchResult(page, rects, context); + if (!rects.isEmpty()) + newSearchResults << QPdfSearchResult(page, rects, contextBefore, contextAfter); } FPDFText_FindClose(sh); FPDFText_ClosePage(textPage); diff --git a/src/pdf/qpdfsearchresult.cpp b/src/pdf/qpdfsearchresult.cpp index 1164a1d43..53da1c165 100644 --- a/src/pdf/qpdfsearchresult.cpp +++ b/src/pdf/qpdfsearchresult.cpp @@ -42,15 +42,20 @@ QT_BEGIN_NAMESPACE QPdfSearchResult::QPdfSearchResult() : QPdfSearchResult(new QPdfSearchResultPrivate()) { } -QPdfSearchResult::QPdfSearchResult(int page, QVector<QRectF> rects, QString context) : - QPdfSearchResult(new QPdfSearchResultPrivate(page, rects, context)) { } +QPdfSearchResult::QPdfSearchResult(int page, QVector<QRectF> rects, QString contextBefore, QString contextAfter) : + QPdfSearchResult(new QPdfSearchResultPrivate(page, rects, contextBefore, contextAfter)) { } QPdfSearchResult::QPdfSearchResult(QPdfSearchResultPrivate *d) : QPdfDestination(static_cast<QPdfDestinationPrivate *>(d)) { } -QString QPdfSearchResult::context() const +QString QPdfSearchResult::contextBefore() const { - return static_cast<QPdfSearchResultPrivate *>(d.data())->context; + return static_cast<QPdfSearchResultPrivate *>(d.data())->contextBefore; +} + +QString QPdfSearchResult::contextAfter() const +{ + return static_cast<QPdfSearchResultPrivate *>(d.data())->contextAfter; } QVector<QRectF> QPdfSearchResult::rectangles() const @@ -63,7 +68,8 @@ QDebug operator<<(QDebug dbg, const QPdfSearchResult &searchResult) QDebugStateSaver saver(dbg); dbg.nospace(); dbg << "QPdfSearchResult(page=" << searchResult.page() - << " context=" << searchResult.context() + << " contextBefore=" << searchResult.contextBefore() + << " contextAfter=" << searchResult.contextAfter() << " rects=" << searchResult.rectangles(); dbg << ')'; return dbg; |