Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/graphic/VectorGraphicSearch.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 */
10
11
#include <utility>
12
#include <vcl/VectorGraphicSearch.hxx>
13
14
#include <vcl/filter/PDFiumLibrary.hxx>
15
#include <tools/UnitConversion.hxx>
16
17
#include <sal/config.h>
18
19
namespace
20
{
21
class SearchContext
22
{
23
private:
24
    std::unique_ptr<vcl::pdf::PDFiumDocument>& mpPdfDocument;
25
    std::unique_ptr<vcl::pdf::PDFiumPage> mpPage;
26
    std::unique_ptr<vcl::pdf::PDFiumTextPage> mpTextPage;
27
    std::unique_ptr<vcl::pdf::PDFiumSearchHandle> mpSearchHandle;
28
29
public:
30
    sal_Int32 mnPageIndex;
31
    int mnCurrentIndex;
32
    OUString maSearchString;
33
    VectorGraphicSearchOptions maOptions;
34
35
    SearchContext(std::unique_ptr<vcl::pdf::PDFiumDocument>& pPdfDocument, sal_Int32 nPageIndex)
36
0
        : mpPdfDocument(pPdfDocument)
37
0
        , mnPageIndex(nPageIndex)
38
0
        , mnCurrentIndex(-1)
39
0
    {
40
0
    }
41
42
    ~SearchContext()
43
0
    {
44
0
        if (mpSearchHandle)
45
0
            mpSearchHandle.reset();
46
0
        if (mpTextPage)
47
0
            mpTextPage.reset();
48
0
        if (mpPage)
49
0
            mpPage.reset();
50
0
    }
51
52
    basegfx::B2DSize getPageSize()
53
0
    {
54
0
        basegfx::B2DSize aSize;
55
0
        if (!mpPdfDocument)
56
0
            return aSize;
57
58
0
        basegfx::B2DSize aPDFSize = mpPdfDocument->getPageSize(mnPageIndex);
59
0
        aSize = basegfx::B2DSize(convertPointToMm100(aPDFSize.getWidth()),
60
0
                                 convertPointToMm100(aPDFSize.getHeight()));
61
0
        return aSize;
62
0
    }
63
64
    bool initialize(OUString const& rSearchString, VectorGraphicSearchOptions const& rOptions)
65
0
    {
66
0
        if (!mpPdfDocument)
67
0
            return false;
68
69
0
        if (rSearchString == maSearchString)
70
0
            return true;
71
72
0
        if (mpSearchHandle)
73
0
            mpSearchHandle.reset();
74
75
0
        if (mpTextPage)
76
0
            mpTextPage.reset();
77
78
0
        if (mpPage)
79
0
            mpPage.reset();
80
81
0
        maSearchString = rSearchString;
82
0
        maOptions = rOptions;
83
84
0
        mpPage = mpPdfDocument->openPage(mnPageIndex);
85
0
        if (!mpPage)
86
0
            return false;
87
88
0
        mpTextPage = mpPage->getTextPage();
89
0
        if (!mpTextPage)
90
0
            return false;
91
92
        // Index where to start to search. -1 => at the end
93
0
        int nStartIndex = maOptions.meStartPosition == SearchStartPosition::End ? -1 : 0;
94
95
0
        if (mnCurrentIndex >= 0)
96
0
            nStartIndex = mnCurrentIndex;
97
98
        // vcl::pdf::PDFFindFlags::MatchCase, vcl::pdf::PDFFindFlags::MatchWholeWord, vcl::pdf::PDFFindFlags::Consecutive
99
        // vcl::pdf::PDFFindFlags::MatchCase - If not set, it will not match case by default.
100
        // vcl::pdf::PDFFindFlags::MatchWholeWord - If not set, it will not match the whole word by default.
101
        // vcl::pdf::PDFFindFlags::Consecutive - If not set, it will skip past the current match to look for the next match.
102
0
        vcl::pdf::PDFFindFlags nSearchFlags{};
103
0
        if (maOptions.mbMatchCase)
104
0
            nSearchFlags |= vcl::pdf::PDFFindFlags::MatchCase;
105
0
        if (maOptions.mbMatchWholeWord)
106
0
            nSearchFlags |= vcl::pdf::PDFFindFlags::MatchWholeWord;
107
108
0
        mpSearchHandle = mpTextPage->findStart(maSearchString, nSearchFlags, nStartIndex);
109
110
0
        return mpSearchHandle != nullptr;
111
0
    }
112
113
    bool next()
114
0
    {
115
0
        if (mpSearchHandle && mpSearchHandle->findNext())
116
0
        {
117
0
            mnCurrentIndex = index();
118
0
            return true;
119
0
        }
120
0
        return false;
121
0
    }
122
123
    bool previous()
124
0
    {
125
0
        if (mpSearchHandle && mpSearchHandle->findPrev())
126
0
        {
127
0
            mnCurrentIndex = index();
128
0
            return true;
129
0
        }
130
0
        return false;
131
0
    }
132
133
    int index()
134
0
    {
135
0
        if (mpSearchHandle)
136
0
            return mpSearchHandle->getSearchResultIndex();
137
0
        return -1;
138
0
    }
139
140
    int size()
141
0
    {
142
0
        if (mpSearchHandle)
143
0
            return mpSearchHandle->getSearchCount();
144
0
        return -1;
145
0
    }
146
147
    std::vector<basegfx::B2DRectangle> getTextRectangles()
148
0
    {
149
0
        std::vector<basegfx::B2DRectangle> aRectangles;
150
151
0
        if (!mpTextPage || !mpSearchHandle)
152
0
            return aRectangles;
153
154
0
        int nIndex = index();
155
0
        if (nIndex < 0)
156
0
            return aRectangles;
157
158
0
        int nSize = size();
159
0
        if (nSize <= 0)
160
0
            return aRectangles;
161
162
0
        double fPageHeight = getPageSize().getHeight();
163
164
0
        for (int nCount = 0; nCount < nSize; nCount++)
165
0
        {
166
0
            basegfx::B2DRectangle aRectangle = mpTextPage->getCharBox(nIndex + nCount, fPageHeight);
167
0
            if (!aRectangle.isEmpty())
168
0
            {
169
0
                aRectangles.push_back(aRectangle);
170
0
            }
171
0
        }
172
173
0
        return aRectangles;
174
0
    }
175
};
176
177
} // end anonymous namespace
178
179
class VectorGraphicSearch::Implementation
180
{
181
public:
182
    std::shared_ptr<vcl::pdf::PDFium> mpPDFium;
183
    std::unique_ptr<vcl::pdf::PDFiumDocument> mpPdfDocument;
184
185
    std::unique_ptr<SearchContext> mpSearchContext;
186
187
    Implementation()
188
0
        : mpPDFium(vcl::pdf::PDFiumLibrary::get())
189
0
    {
190
0
    }
191
192
0
    ~Implementation() { mpSearchContext.reset(); }
193
};
194
195
VectorGraphicSearch::VectorGraphicSearch(Graphic aGraphic)
196
0
    : mpImplementation(std::make_unique<VectorGraphicSearch::Implementation>())
197
0
    , maGraphic(std::move(aGraphic))
198
0
{
199
0
}
200
201
0
VectorGraphicSearch::~VectorGraphicSearch() { mpImplementation.reset(); }
202
203
bool VectorGraphicSearch::search(OUString const& rSearchString,
204
                                 VectorGraphicSearchOptions const& rOptions)
205
0
{
206
0
    if (!mpImplementation->mpPDFium)
207
0
    {
208
0
        return false;
209
0
    }
210
211
0
    if (!mpImplementation->mpSearchContext)
212
0
    {
213
0
        auto pData = maGraphic.getVectorGraphicData();
214
215
0
        if (pData && pData->getType() == VectorGraphicDataType::Pdf)
216
0
        {
217
0
            if (searchPDF(pData))
218
0
            {
219
0
                return mpImplementation->mpSearchContext->initialize(rSearchString, rOptions);
220
0
            }
221
0
        }
222
0
        return false;
223
0
    }
224
0
    return mpImplementation->mpSearchContext->initialize(rSearchString, rOptions);
225
0
}
226
227
bool VectorGraphicSearch::searchPDF(std::shared_ptr<VectorGraphicData> const& rData)
228
0
{
229
0
    if (!mpImplementation->mpPDFium)
230
0
    {
231
0
        return false;
232
0
    }
233
234
0
    mpImplementation->mpPdfDocument = mpImplementation->mpPDFium->openDocument(
235
0
        rData->getBinaryDataContainer().getData(), rData->getBinaryDataContainer().getSize(),
236
0
        OString());
237
238
0
    if (!mpImplementation->mpPdfDocument)
239
0
    {
240
        //TODO: Handle failure to load.
241
0
        switch (mpImplementation->mpPDFium->getLastErrorCode())
242
0
        {
243
0
            case vcl::pdf::PDFErrorType::Success:
244
0
                break;
245
0
            case vcl::pdf::PDFErrorType::Unknown:
246
0
                break;
247
0
            case vcl::pdf::PDFErrorType::File:
248
0
                break;
249
0
            case vcl::pdf::PDFErrorType::Format:
250
0
                break;
251
0
            case vcl::pdf::PDFErrorType::Password:
252
0
                break;
253
0
            case vcl::pdf::PDFErrorType::Security:
254
0
                break;
255
0
            case vcl::pdf::PDFErrorType::Page:
256
0
                break;
257
0
            default:
258
0
                break;
259
0
        }
260
0
        return false;
261
0
    }
262
263
0
    sal_Int32 nPageIndex = std::max(rData->getPageIndex(), sal_Int32(0));
264
265
0
    mpImplementation->mpSearchContext.reset(
266
0
        new SearchContext(mpImplementation->mpPdfDocument, nPageIndex));
267
0
    return true;
268
0
}
269
270
basegfx::B2DSize VectorGraphicSearch::pageSize()
271
0
{
272
0
    basegfx::B2DSize aSize;
273
0
    if (mpImplementation->mpSearchContext)
274
0
        aSize = mpImplementation->mpSearchContext->getPageSize();
275
0
    return aSize;
276
0
}
277
278
bool VectorGraphicSearch::next()
279
0
{
280
0
    if (mpImplementation->mpSearchContext)
281
0
        return mpImplementation->mpSearchContext->next();
282
0
    return false;
283
0
}
284
285
bool VectorGraphicSearch::previous()
286
0
{
287
0
    if (mpImplementation->mpSearchContext)
288
0
        return mpImplementation->mpSearchContext->previous();
289
0
    return false;
290
0
}
291
292
int VectorGraphicSearch::index()
293
0
{
294
0
    if (mpImplementation->mpSearchContext)
295
0
        return mpImplementation->mpSearchContext->index();
296
0
    return -1;
297
0
}
298
299
std::vector<basegfx::B2DRectangle> VectorGraphicSearch::getTextRectangles()
300
0
{
301
0
    if (mpImplementation->mpSearchContext)
302
0
        return mpImplementation->mpSearchContext->getTextRectangles();
303
304
0
    return std::vector<basegfx::B2DRectangle>();
305
0
}
306
307
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */