summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2020-02-25 17:49:56 +0100
committerShawn Rutledge <shawn.rutledge@qt.io>2020-02-26 15:40:24 +0100
commit926afcaa6f3ce02b51cd535d82454f4b25e0b7f6 (patch)
tree66773defe3e53fc4a37f3fc10eb2f2649d27f668
parent8091944ae8ccfb4e0b3e7518912802c9c75704d0 (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.qml26
-rw-r--r--examples/pdf/pdfviewer/viewer.qml26
-rw-r--r--src/pdf/api/qpdfsearchmodel.h3
-rw-r--r--src/pdf/api/qpdfsearchresult.h8
-rw-r--r--src/pdf/api/qpdfsearchresult_p.h8
-rw-r--r--src/pdf/qpdfsearchmodel.cpp42
-rw-r--r--src/pdf/qpdfsearchresult.cpp16
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;