Coverage Report

Created: 2026-02-14 09:37

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