Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svgio/source/svgreader/svgpatternnode.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
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <svgpatternnode.hxx>
21
#include <svgdocument.hxx>
22
#include <o3tl/string_view.hxx>
23
24
namespace svgio::svgreader
25
{
26
        void SvgPatternNode::tryToFindLink()
27
0
        {
28
0
            if(!mpXLink && !maXLink.isEmpty())
29
0
            {
30
0
                mpXLink = dynamic_cast< const SvgPatternNode* >(getDocument().findSvgNodeById(maXLink));
31
0
            }
32
0
        }
33
34
        SvgPatternNode::SvgPatternNode(
35
            SvgDocument& rDocument,
36
            SvgNode* pParent)
37
0
        :   SvgNode(SVGToken::Pattern, rDocument, pParent),
38
0
            maSvgStyleAttributes(*this),
39
0
            mbResolvingLink(false),
40
0
            mpXLink(nullptr)
41
0
        {
42
0
        }
43
44
        SvgPatternNode::~SvgPatternNode()
45
0
        {
46
0
        }
47
48
        const SvgStyleAttributes* SvgPatternNode::getSvgStyleAttributes() const
49
0
        {
50
0
            return checkForCssStyle(maSvgStyleAttributes);
51
0
        }
52
53
        void SvgPatternNode::parseAttribute(SVGToken aSVGToken, const OUString& aContent)
54
0
        {
55
            // call parent
56
0
            SvgNode::parseAttribute(aSVGToken, aContent);
57
58
            // read style attributes
59
0
            maSvgStyleAttributes.parseStyleAttribute(aSVGToken, aContent);
60
61
            // parse own
62
0
            switch(aSVGToken)
63
0
            {
64
0
                case SVGToken::Style:
65
0
                {
66
0
                    readLocalCssStyle(aContent);
67
0
                    break;
68
0
                }
69
0
                case SVGToken::ViewBox:
70
0
                {
71
0
                    const basegfx::B2DRange aRange(readViewBox(aContent, *this));
72
73
0
                    if(!aRange.isEmpty())
74
0
                    {
75
0
                        setViewBox(&aRange);
76
0
                    }
77
0
                    break;
78
0
                }
79
0
                case SVGToken::PreserveAspectRatio:
80
0
                {
81
0
                    maSvgAspectRatio = readSvgAspectRatio(aContent);
82
0
                    break;
83
0
                }
84
0
                case SVGToken::X:
85
0
                {
86
0
                    SvgNumber aNum;
87
88
0
                    if(readSingleNumber(aContent, aNum))
89
0
                    {
90
0
                        maX = aNum;
91
0
                    }
92
0
                    break;
93
0
                }
94
0
                case SVGToken::Y:
95
0
                {
96
0
                    SvgNumber aNum;
97
98
0
                    if(readSingleNumber(aContent, aNum))
99
0
                    {
100
0
                        maY = aNum;
101
0
                    }
102
0
                    break;
103
0
                }
104
0
                case SVGToken::Width:
105
0
                {
106
0
                    SvgNumber aNum;
107
108
0
                    if(readSingleNumber(aContent, aNum))
109
0
                    {
110
0
                        if(aNum.isPositive())
111
0
                        {
112
0
                            maWidth = aNum;
113
0
                        }
114
0
                    }
115
0
                    break;
116
0
                }
117
0
                case SVGToken::Height:
118
0
                {
119
0
                    SvgNumber aNum;
120
121
0
                    if(readSingleNumber(aContent, aNum))
122
0
                    {
123
0
                        if(aNum.isPositive())
124
0
                        {
125
0
                            maHeight = aNum;
126
0
                        }
127
0
                    }
128
0
                    break;
129
0
                }
130
0
                case SVGToken::PatternUnits:
131
0
                {
132
0
                    if(!aContent.isEmpty())
133
0
                    {
134
0
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), commonStrings::aStrUserSpaceOnUse))
135
0
                        {
136
0
                            setPatternUnits(SvgUnits::userSpaceOnUse);
137
0
                        }
138
0
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), commonStrings::aStrObjectBoundingBox))
139
0
                        {
140
0
                            setPatternUnits(SvgUnits::objectBoundingBox);
141
0
                        }
142
0
                    }
143
0
                    break;
144
0
                }
145
0
                case SVGToken::PatternContentUnits:
146
0
                {
147
0
                    if(!aContent.isEmpty())
148
0
                    {
149
0
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), commonStrings::aStrUserSpaceOnUse))
150
0
                        {
151
0
                            setPatternContentUnits(SvgUnits::userSpaceOnUse);
152
0
                        }
153
0
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), commonStrings::aStrObjectBoundingBox))
154
0
                        {
155
0
                            setPatternContentUnits(SvgUnits::objectBoundingBox);
156
0
                        }
157
0
                    }
158
0
                    break;
159
0
                }
160
0
                case SVGToken::PatternTransform:
161
0
                {
162
0
                    const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this));
163
164
0
                    if(!aMatrix.isIdentity())
165
0
                    {
166
0
                        setPatternTransform(aMatrix);
167
0
                    }
168
0
                    break;
169
0
                }
170
0
                case SVGToken::Href:
171
0
                case SVGToken::XlinkHref:
172
0
                {
173
0
                    readLocalLink(aContent, maXLink);
174
0
                    tryToFindLink();
175
0
                    break;
176
0
                }
177
0
                default:
178
0
                {
179
0
                    break;
180
0
                }
181
0
            }
182
0
        }
183
184
        void SvgPatternNode::getValuesRelative(double& rfX, double& rfY, double& rfW, double& rfH, const basegfx::B2DRange& rGeoRange, SvgNode const & rUser) const
185
0
        {
186
0
            double fTargetWidth(rGeoRange.getWidth());
187
0
            double fTargetHeight(rGeoRange.getHeight());
188
189
0
            if(fTargetWidth <= 0.0 || fTargetHeight <= 0.0)
190
0
                return;
191
192
0
            const SvgUnits aPatternUnits(getPatternUnits() ? *getPatternUnits() : SvgUnits::objectBoundingBox);
193
194
0
            if (SvgUnits::objectBoundingBox == aPatternUnits)
195
0
            {
196
0
                rfW = (getWidth().isSet()) ? getWidth().getNumber() : 0.0;
197
0
                rfH = (getHeight().isSet()) ? getHeight().getNumber() : 0.0;
198
199
0
                if(SvgUnit::percent == getWidth().getUnit())
200
0
                {
201
0
                    rfW *= 0.01;
202
0
                }
203
204
0
                if(SvgUnit::percent == getHeight().getUnit())
205
0
                {
206
0
                    rfH *= 0.01;
207
0
                }
208
0
            }
209
0
            else
210
0
            {
211
0
                rfW = (getWidth().isSet()) ? getWidth().solve(rUser, NumberType::xcoordinate) : 0.0;
212
0
                rfH = (getHeight().isSet()) ? getHeight().solve(rUser, NumberType::ycoordinate) : 0.0;
213
214
                // make relative to rGeoRange
215
0
                rfW /= fTargetWidth;
216
0
                rfH /= fTargetHeight;
217
0
            }
218
219
0
            if(rfW <= 0.0 || rfH <= 0.0)
220
0
                return;
221
222
0
            if (SvgUnits::objectBoundingBox == aPatternUnits)
223
0
            {
224
0
                rfX = (getX().isSet()) ? getX().getNumber() : 0.0;
225
0
                rfY = (getY().isSet()) ? getY().getNumber() : 0.0;
226
227
0
                if(SvgUnit::percent == getX().getUnit())
228
0
                {
229
0
                    rfX *= 0.01;
230
0
                }
231
232
0
                if(SvgUnit::percent == getY().getUnit())
233
0
                {
234
0
                    rfY *= 0.01;
235
0
                }
236
0
            }
237
0
            else
238
0
            {
239
0
                rfX = (getX().isSet()) ? getX().solve(rUser, NumberType::xcoordinate) : 0.0;
240
0
                rfY = (getY().isSet()) ? getY().solve(rUser, NumberType::ycoordinate) : 0.0;
241
242
                // make relative to rGeoRange
243
0
                rfX = (rfX - rGeoRange.getMinX()) / fTargetWidth;
244
0
                rfY = (rfY - rGeoRange.getMinY()) / fTargetHeight;
245
0
            }
246
0
        }
247
248
        const drawinglayer::primitive2d::Primitive2DContainer& SvgPatternNode::getPatternPrimitives() const
249
0
        {
250
0
            if(aPrimitives.empty() && Display::None != getDisplay())
251
0
            {
252
0
                decomposeSvgNode(const_cast< SvgPatternNode* >(this)->aPrimitives, true);
253
0
            }
254
255
0
            if(aPrimitives.empty() && !maXLink.isEmpty())
256
0
            {
257
0
                const_cast< SvgPatternNode* >(this)->tryToFindLink();
258
259
0
                if (mpXLink && !mbResolvingLink)
260
0
                {
261
0
                    mbResolvingLink = true;
262
0
                    const drawinglayer::primitive2d::Primitive2DContainer& ret = mpXLink->getPatternPrimitives();
263
0
                    mbResolvingLink = false;
264
0
                    return ret;
265
0
                }
266
0
            }
267
268
0
            return aPrimitives;
269
0
        }
270
271
        basegfx::B2DRange SvgPatternNode::getCurrentViewPort() const
272
0
        {
273
0
            if(getViewBox())
274
0
            {
275
0
                return *(getViewBox());
276
0
            }
277
0
            else
278
0
            {
279
0
                return SvgNode::getCurrentViewPort();
280
0
            }
281
0
        }
282
283
        const basegfx::B2DRange* SvgPatternNode::getViewBox() const
284
0
        {
285
0
            if(mpViewBox)
286
0
            {
287
0
                return mpViewBox.get();
288
0
            }
289
290
0
            const_cast< SvgPatternNode* >(this)->tryToFindLink();
291
292
0
            if (mpXLink && !mbResolvingLink)
293
0
            {
294
0
                mbResolvingLink = true;
295
0
                auto ret = mpXLink->getViewBox();
296
0
                mbResolvingLink = false;
297
0
                return ret;
298
0
            }
299
300
0
            return nullptr;
301
0
        }
302
303
        const SvgAspectRatio& SvgPatternNode::getSvgAspectRatio() const
304
0
        {
305
0
            if(maSvgAspectRatio.isSet())
306
0
            {
307
0
                return maSvgAspectRatio;
308
0
            }
309
310
0
            const_cast< SvgPatternNode* >(this)->tryToFindLink();
311
312
0
            if (mpXLink && !mbResolvingLink)
313
0
            {
314
0
                mbResolvingLink = true;
315
0
                const SvgAspectRatio& ret = mpXLink->getSvgAspectRatio();
316
0
                mbResolvingLink = false;
317
0
                return ret;
318
0
            }
319
320
0
            return maSvgAspectRatio;
321
0
        }
322
323
        const SvgNumber& SvgPatternNode::getX() const
324
0
        {
325
0
            if(maX.isSet())
326
0
            {
327
0
                return maX;
328
0
            }
329
330
0
            const_cast< SvgPatternNode* >(this)->tryToFindLink();
331
332
0
            if (mpXLink && !mbResolvingLink)
333
0
            {
334
0
                mbResolvingLink = true;
335
0
                const SvgNumber& ret = mpXLink->getX();
336
0
                mbResolvingLink = false;
337
0
                return ret;
338
0
            }
339
340
0
            return maX;
341
0
        }
342
343
        const SvgNumber& SvgPatternNode::getY() const
344
0
        {
345
0
            if(maY.isSet())
346
0
            {
347
0
                return maY;
348
0
            }
349
350
0
            const_cast< SvgPatternNode* >(this)->tryToFindLink();
351
352
0
            if (mpXLink && !mbResolvingLink)
353
0
            {
354
0
                mbResolvingLink = true;
355
0
                const SvgNumber& ret = mpXLink->getY();
356
0
                mbResolvingLink = false;
357
0
                return ret;
358
0
            }
359
360
0
            return maY;
361
0
        }
362
363
        const SvgNumber& SvgPatternNode::getWidth() const
364
0
        {
365
0
            if(maWidth.isSet())
366
0
            {
367
0
                return maWidth;
368
0
            }
369
370
0
            const_cast< SvgPatternNode* >(this)->tryToFindLink();
371
372
0
            if (mpXLink && !mbResolvingLink)
373
0
            {
374
0
                mbResolvingLink = true;
375
0
                const SvgNumber& ret = mpXLink->getWidth();
376
0
                mbResolvingLink = false;
377
0
                return ret;
378
0
            }
379
380
0
            return maWidth;
381
0
        }
382
383
        const SvgNumber& SvgPatternNode::getHeight() const
384
0
        {
385
0
            if(maHeight.isSet())
386
0
            {
387
0
                return maHeight;
388
0
            }
389
390
0
            const_cast< SvgPatternNode* >(this)->tryToFindLink();
391
392
0
            if (mpXLink && !mbResolvingLink)
393
0
            {
394
0
                mbResolvingLink = true;
395
0
                const SvgNumber& ret = mpXLink->getHeight();
396
0
                mbResolvingLink = false;
397
0
                return ret;
398
0
            }
399
400
0
            return maHeight;
401
0
        }
402
403
        const SvgUnits* SvgPatternNode::getPatternUnits() const
404
0
        {
405
0
            if(moPatternUnits)
406
0
            {
407
0
                return &*moPatternUnits;
408
0
            }
409
410
0
            const_cast< SvgPatternNode* >(this)->tryToFindLink();
411
412
0
            if (mpXLink && !mbResolvingLink)
413
0
            {
414
0
                mbResolvingLink = true;
415
0
                auto ret = mpXLink->getPatternUnits();
416
0
                mbResolvingLink = false;
417
0
                return ret;
418
0
            }
419
420
0
            return nullptr;
421
0
        }
422
423
        const SvgUnits* SvgPatternNode::getPatternContentUnits() const
424
0
        {
425
0
            if(moPatternContentUnits)
426
0
            {
427
0
                return &*moPatternContentUnits;
428
0
            }
429
430
0
            const_cast< SvgPatternNode* >(this)->tryToFindLink();
431
432
0
            if (mpXLink && !mbResolvingLink)
433
0
            {
434
0
                mbResolvingLink = true;
435
0
                auto ret = mpXLink->getPatternContentUnits();
436
0
                mbResolvingLink = false;
437
0
                return ret;
438
0
            }
439
440
0
            return nullptr;
441
0
        }
442
443
        std::optional<basegfx::B2DHomMatrix> SvgPatternNode::getPatternTransform() const
444
0
        {
445
0
            if(mpaPatternTransform)
446
0
            {
447
0
                return mpaPatternTransform;
448
0
            }
449
450
0
            const_cast< SvgPatternNode* >(this)->tryToFindLink();
451
452
0
            if (mpXLink && !mbResolvingLink)
453
0
            {
454
0
                mbResolvingLink = true;
455
0
                auto ret = mpXLink->getPatternTransform();
456
0
                mbResolvingLink = false;
457
0
                return ret;
458
0
            }
459
460
0
            return std::nullopt;
461
0
        }
462
463
} // end of namespace svgio::svgreader
464
465
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */