-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 Hello again,
Am 11.01.2015 um 00:57 schrieb Albert Astals Cid: > El Diumenge, 11 de gener de 2015, a les 00:37:36, Adam Reichold va > escriure: >> Hello, >> >> Am 10.01.2015 um 23:45 schrieb Albert Astals Cid: >>> Maybe you can add some tests to check_search.cpp? >> >> Attached patch with a test case for the four flag combinations >> added to the Qt4 and Qt5 versions of "check_search.cpp". >> >> Of course, this very much highlights the ugliness of shoe horning >> the flags into the existing enumeration. But I don't think that >> making "Poppler::Page::search" take an "int" instead of >> "SearchMode" would be ABI compatible strictly speaking, since now >> the compiler is free to choose the underlying type for the >> enumeration. > > Meh, you're right, this is not good either since you're passing in > SearchMode something that is not a SearchMode (it's the sum of > two). > > So yeah it'd be better to go the QFlags way. Maybe you can leave > the old function there, mark it as deprecated and say that it only > obeys the sensitiviness part and add a new function that accepts > the qflags? Ok, attached is the patch which adds a new flags type SearchFlags and corresponding overloads to the Qt4 frontend. I try to factor out as much common code as possible and add a test case as well. I'll add the Qt5 frontend as soon we think the approach is sound. I chose to use a new enumeration SearchFlag instead of SearchMode since the symmetry of SearchMode does not really make sense if there are several non-exclusive flags. Best regards, Adam. > Cheers, Albert > >> >> Best regards, Adam. >> >>> Cheers, Albert >>> >>> El Dissabte, 10 de gener de 2015, a les 23:26:01, Adam Reichold >>> va >>> >>> escriure: >>>> Hello again, >>>> >>>> Am 10.01.2015 um 22:25 schrieb Albert Astals Cid: >>>>> El Dissabte, 10 de gener de 2015, a les 17:51:48, Adam >>>>> Reichold va >>>>> >>>>> escriure: >>>>>> Hello, >>>>>> >>>>>> Attach is a patch that would expose Poppler's >>>>>> whole-words search option within the Qt frontends. >>>>>> >>>>>> While the implementation seems straight forward so far, >>>>>> I would like to request comments whether extending the >>>>>> "SearchMode" enumeration to flag definition is >>>>>> considered harmless. The proper way to do it seems to be >>>>>> the introduction of "QFlags<SearchMode>" which would >>>>>> however break compatibility and hence imply the need to >>>>>> have an additional overloads using a separate flag (and >>>>>> enumeration?) definition. >>>>> >>>>> Looks good to me. The qt5/ side would need the >>>>> documentation update too, no? >>>> >>>> patch with fixed Qt5 documentation and "\since" commands >>>> attached. >>>> >>>> Best regards, Adam. >>>> >>>>> Cheers, Albert >>>>> >>>>>> Best regards, Adam. >>>>> >>>>> _______________________________________________ poppler >>>>> mailing list [email protected] >>>>> http://lists.freedesktop.org/mailman/listinfo/poppler >>> >>> _______________________________________________ poppler >>> mailing list [email protected] >>> http://lists.freedesktop.org/mailman/listinfo/poppler > > _______________________________________________ poppler mailing > list [email protected] > http://lists.freedesktop.org/mailman/listinfo/poppler > -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAEBCAAGBQJUskzvAAoJEPSSjE3STU34Zt0H/3mCeTF91/udHJ2BfnIAgT3o H4sdxddOCIBTNugH8vI5890HrlyUjb7IsoV3rntfePj/XWUERcCZViecrd3+qLdn AL53Sg0m0lR9pEZA4KOxetM4GVl5flncxpQo9dq9dBFBnMUufOUT2PF8EXKZYyo7 h+te65+fOBOxI25Y2G4nsQxqJFSjhf66nYGJVC6f/JRu28MdTFgp1nzZ3941LRbm pPF4rwZqju/yfF9HtNW1F1wctsoEzTEWQ4QMKf1FpLupFrxyHmMnuD5npbFLOSu6 68+R/K+p8b9PqMyC7F0NaVhlfT39NJ46z7idHlNFiuk8KImzFPunDhecax9OYrQ= =zIMd -----END PGP SIGNATURE-----
>From f06b9a42a536b2699ddf8100b0fa4af41e91a98f Mon Sep 17 00:00:00 2001 From: Adam Reichold <[email protected]> Date: Sun, 11 Jan 2015 11:08:37 +0100 Subject: [PATCH] Expose whole-words find option in Qt4 frontend Adds a SearchFlags flag type and corresponding overloads to the Qt4 frontend so that callers can indicate whole-words matching in addition to case sensitivity. --- qt4/src/poppler-page-private.h | 4 +- qt4/src/poppler-page.cc | 112 ++++++++++++++++++++++++++++------------- qt4/src/poppler-qt4.h | 42 +++++++++++++++- qt4/tests/check_search.cpp | 31 ++++++++++++ 4 files changed, 151 insertions(+), 38 deletions(-) diff --git a/qt4/src/poppler-page-private.h b/qt4/src/poppler-page-private.h index 91955e0..ef467e5 100644 --- a/qt4/src/poppler-page-private.h +++ b/qt4/src/poppler-page-private.h @@ -46,7 +46,9 @@ public: static Link* convertLinkActionToLink(::LinkAction * a, DocumentData *parentDoc, const QRectF &linkArea); - TextPage *prepareTextSearch(const QString &text, Page::SearchMode caseSensitive, Page::Rotation rotate, GBool *sCase, QVector<Unicode> *u); + TextPage *prepareTextSearch(const QString &text, Page::Rotation rotate, QVector<Unicode> *u); + GBool performSingleTextSearch(TextPage* textPage, QVector<Unicode> &u, double &sLeft, double &sTop, double &sRight, double &sBottom, Page::SearchDirection direction, GBool sCase, GBool sWords); + QList<QRectF> performMultipleTextSearch(TextPage* textPage, QVector<Unicode> &u, GBool sCase, GBool sWords); }; } diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc index fc928b9..4a0e689 100644 --- a/qt4/src/poppler-page.cc +++ b/qt4/src/poppler-page.cc @@ -215,16 +215,13 @@ Link* PageData::convertLinkActionToLink(::LinkAction * a, DocumentData *parentDo return popplerLink; } -TextPage *PageData::prepareTextSearch(const QString &text, Page::SearchMode caseSensitive, Page::Rotation rotate, GBool *sCase, QVector<Unicode> *u) +inline TextPage *PageData::prepareTextSearch(const QString &text, Page::Rotation rotate, QVector<Unicode> *u) { const QChar * str = text.unicode(); const int len = text.length(); u->resize(len); for (int i = 0; i < len; ++i) (*u)[i] = str[i].unicode(); - if (caseSensitive == Page::CaseSensitive) *sCase = gTrue; - else *sCase = gFalse; - const int rotation = (int)rotate * 90; // fetch ourselves a textpage @@ -234,7 +231,43 @@ TextPage *PageData::prepareTextSearch(const QString &text, Page::SearchMode case TextPage *textPage=td.takeText(); return textPage; -} +} + +inline GBool PageData::performSingleTextSearch(TextPage* textPage, QVector<Unicode> &u, double &sLeft, double &sTop, double &sRight, double &sBottom, Page::SearchDirection direction, GBool sCase, GBool sWords) +{ + if (direction == Page::FromTop) + return textPage->findText( u.data(), u.size(), + gTrue, gTrue, gFalse, gFalse, sCase, gFalse, sWords, &sLeft, &sTop, &sRight, &sBottom ); + else if ( direction == Page::NextResult ) + return textPage->findText( u.data(), u.size(), + gFalse, gTrue, gTrue, gFalse, sCase, gFalse, sWords, &sLeft, &sTop, &sRight, &sBottom ); + else if ( direction == Page::PreviousResult ) + return textPage->findText( u.data(), u.size(), + gFalse, gTrue, gTrue, gFalse, sCase, gTrue, sWords, &sLeft, &sTop, &sRight, &sBottom ); + + return gFalse; +} + +inline QList<QRectF> PageData::performMultipleTextSearch(TextPage* textPage, QVector<Unicode> &u, GBool sCase, GBool sWords) +{ + QList<QRectF> results; + double sLeft = 0.0, sTop = 0.0, sRight = 0.0, sBottom = 0.0; + + while(textPage->findText( u.data(), u.size(), + gFalse, gTrue, gTrue, gFalse, sCase, gFalse, sWords, &sLeft, &sTop, &sRight, &sBottom )) + { + QRectF result; + + result.setLeft(sLeft); + result.setTop(sTop); + result.setRight(sRight); + result.setBottom(sBottom); + + results.append(result); + } + + return results; +} Page::Page(DocumentData *doc, int index) { m_page = new PageData(); @@ -459,20 +492,27 @@ QString Page::text(const QRectF &r) const bool Page::search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchMode caseSensitive, Rotation rotate) const { - GBool sCase; + const GBool sCase = caseSensitive == Page::CaseSensitive ? gTrue : gFalse; + QVector<Unicode> u; - TextPage *textPage = m_page->prepareTextSearch(text, caseSensitive, rotate, &sCase, &u); - - bool found = false; - if (direction == FromTop) - found = textPage->findText( u.data(), u.size(), - gTrue, gTrue, gFalse, gFalse, sCase, gFalse, gFalse, &sLeft, &sTop, &sRight, &sBottom ); - else if ( direction == NextResult ) - found = textPage->findText( u.data(), u.size(), - gFalse, gTrue, gTrue, gFalse, sCase, gFalse, gFalse, &sLeft, &sTop, &sRight, &sBottom ); - else if ( direction == PreviousResult ) - found = textPage->findText( u.data(), u.size(), - gFalse, gTrue, gTrue, gFalse, sCase, gTrue, gFalse, &sLeft, &sTop, &sRight, &sBottom ); + TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); + + const bool found = m_page->performSingleTextSearch(textPage, u, sLeft, sTop, sRight, sBottom, direction, sCase, gFalse); + + textPage->decRefCnt(); + + return found; +} + +bool Page::search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchFlags flags, Rotation rotate) const +{ + const GBool sCase = flags.testFlag(IgnoreCase) ? gFalse : gTrue; + const GBool sWords = flags.testFlag(WholeWorlds) ? gTrue : gFalse; + + QVector<Unicode> u; + TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); + + const bool found = m_page->performSingleTextSearch(textPage, u, sLeft, sTop, sRight, sBottom, direction, sCase, sWords); textPage->decRefCnt(); @@ -499,31 +539,33 @@ bool Page::search(const QString &text, QRectF &rect, SearchDirection direction, QList<QRectF> Page::search(const QString &text, SearchMode caseSensitive, Rotation rotate) const { - GBool sCase; + const GBool sCase = caseSensitive == Page::CaseSensitive ? gTrue : gFalse; + QVector<Unicode> u; - TextPage *textPage = m_page->prepareTextSearch(text, caseSensitive, rotate, &sCase, &u); + TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); - QList<QRectF> results; - double sLeft = 0.0, sTop = 0.0, sRight = 0.0, sBottom = 0.0; - - while(textPage->findText( u.data(), u.size(), - gFalse, gTrue, gTrue, gFalse, sCase, gFalse, gFalse, &sLeft, &sTop, &sRight, &sBottom )) - { - QRectF result; - - result.setLeft(sLeft); - result.setTop(sTop); - result.setRight(sRight); - result.setBottom(sBottom); - - results.append(result); - } + const QList<QRectF> results = m_page->performMultipleTextSearch(textPage, u, sCase, gFalse); textPage->decRefCnt(); return results; } +QList<QRectF> Page::search(const QString &text, SearchFlags flags, Rotation rotate) const +{ + const GBool sCase = flags.testFlag(IgnoreCase) ? gFalse : gTrue; + const GBool sWords = flags.testFlag(WholeWorlds) ? gTrue : gFalse; + + QVector<Unicode> u; + TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); + + const QList<QRectF> results = m_page->performMultipleTextSearch(textPage, u, sCase, sWords); + + textPage->decRefCnt(); + + return results; +} + QList<TextBox*> Page::textList(Rotation rotate) const { TextOutputDev *output_dev; diff --git a/qt4/src/poppler-qt4.h b/qt4/src/poppler-qt4.h index ee7558e..193b043 100644 --- a/qt4/src/poppler-qt4.h +++ b/qt4/src/poppler-qt4.h @@ -579,6 +579,16 @@ delete it; enum SearchMode { CaseSensitive, ///< Case differences cause no match in searching CaseInsensitive ///< Case differences are ignored in matching }; + + /** + Flags to modify the search behaviour \since 0.31 + */ + enum SearchFlag + { + IgnoreCase = 0x00000001, ///< Case differences are ignored + WholeWorlds = 0x00000002 ///< Only whole words are matched + }; + Q_DECLARE_FLAGS( SearchFlags, SearchFlag ) /** Returns true if the specified text was found. @@ -603,7 +613,21 @@ delete it; \param rotate the rotation to apply for the search order \since 0.14 **/ - bool search(const QString &text, double &rectLeft, double &rectTop, double &rectRight, double &rectBottom, SearchDirection direction, SearchMode caseSensitive, Rotation rotate = Rotate0) const; + Q_DECL_DEPRECATED bool search(const QString &text, double &rectLeft, double &rectTop, double &rectRight, double &rectBottom, SearchDirection direction, SearchMode caseSensitive, Rotation rotate = Rotate0) const; + + /** + Returns true if the specified text was found. + + \param text the text the search + \param rectXXX in all directions is used to return where the text was found, for NextResult and PreviousResult + indicates where to continue searching for + \param direction in which direction do the search + \param flags the flags to consider during matching + \param rotate the rotation to apply for the search order + + \since 0.31 + **/ + bool search(const QString &text, double &rectLeft, double &rectTop, double &rectRight, double &rectBottom, SearchDirection direction, SearchFlags flags = 0, Rotation rotate = Rotate0) const; /** Returns a list of all occurrences of the specified text on the page. @@ -616,7 +640,20 @@ delete it; \since 0.22 **/ - QList<QRectF> search(const QString &text, SearchMode caseSensitive, Rotation rotate = Rotate0) const; + Q_DECL_DEPRECATED QList<QRectF> search(const QString &text, SearchMode caseSensitive, Rotation rotate = Rotate0) const; + + /** + Returns a list of all occurrences of the specified text on the page. + + \param text the text to search + \param flags the flags to consider during matching + \param rotate the rotation to apply for the search order + + \warning Do not use the returned QRectF as arguments of another search call because of truncation issues if qreal is defined as float. + + \since 0.31 + **/ + QList<QRectF> search(const QString &text, SearchFlags flags = 0, Rotation rotate = Rotate0) const; /** Returns a list of text of the page @@ -1826,6 +1863,7 @@ height = dummy.height(); } Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::Page::PainterFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::Page::SearchFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::Document::RenderHints) Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::PDFConverter::PDFOptions) Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::PSConverter::PSOptions) diff --git a/qt4/tests/check_search.cpp b/qt4/tests/check_search.cpp index cabf82d..98a1cec 100644 --- a/qt4/tests/check_search.cpp +++ b/qt4/tests/check_search.cpp @@ -8,6 +8,7 @@ class TestSearch: public QObject private slots: void bug7063(); void testNextAndPrevious(); + void testWholeWordsOnly(); }; void TestSearch::bug7063() @@ -86,6 +87,36 @@ void TestSearch::testNextAndPrevious() delete doc; } +void TestSearch::testWholeWordsOnly() +{ + QScopedPointer< Poppler::Document > document(Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf")); + QVERIFY( document ); + + QScopedPointer< Poppler::Page > page(document->page(0)); + QVERIFY( page ); + + const Poppler::Page::SearchDirection direction = Poppler::Page::FromTop; + + const Poppler::Page::SearchFlags mode0 = 0; + const Poppler::Page::SearchFlags mode1 = Poppler::Page::IgnoreCase; + const Poppler::Page::SearchFlags mode2 = Poppler::Page::WholeWorlds; + const Poppler::Page::SearchFlags mode3 = Poppler::Page::IgnoreCase | Poppler::Page::WholeWorlds; + + double left, top, right, bottom; + + QCOMPARE( page->search(QLatin1String("brown"), left, top, right, bottom, direction, mode0), true ); + QCOMPARE( page->search(QLatin1String("brOwn"), left, top, right, bottom, direction, mode0), false ); + + QCOMPARE( page->search(QLatin1String("brOwn"), left, top, right, bottom, direction, mode1), true ); + QCOMPARE( page->search(QLatin1String("brawn"), left, top, right, bottom, direction, mode1), false ); + + QCOMPARE( page->search(QLatin1String("brown"), left, top, right, bottom, direction, mode2), true ); + QCOMPARE( page->search(QLatin1String("own"), left, top, right, bottom, direction, mode2), false ); + + QCOMPARE( page->search(QLatin1String("brOwn"), left, top, right, bottom, direction, mode3), true ); + QCOMPARE( page->search(QLatin1String("Own"), left, top, right, bottom, direction, mode3), false ); +} + QTEST_MAIN(TestSearch) #include "check_search.moc" -- 2.2.1
_______________________________________________ poppler mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/poppler
