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/svggradientnode.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 <svggradientnode.hxx>
21
#include <svgdocument.hxx>
22
#include <svggradientstopnode.hxx>
23
#include <o3tl/string_view.hxx>
24
#include <osl/diagnose.h>
25
26
namespace svgio::svgreader
27
{
28
        void SvgGradientNode::tryToFindLink()
29
0
        {
30
0
            if(!mpXLink && !maXLink.isEmpty())
31
0
            {
32
0
                mpXLink = dynamic_cast< const SvgGradientNode* >(getDocument().findSvgNodeById(maXLink));
33
0
            }
34
0
        }
35
36
        SvgGradientNode::SvgGradientNode(
37
            SVGToken aType,
38
            SvgDocument& rDocument,
39
            SvgNode* pParent)
40
0
        :   SvgNode(aType, rDocument, pParent),
41
0
            maSvgStyleAttributes(*this),
42
0
            maGradientUnits(SvgUnits::objectBoundingBox),
43
0
            maSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Pad),
44
0
            mbResolvingLink(false),
45
0
            mpXLink(nullptr)
46
0
        {
47
0
            OSL_ENSURE(aType == SVGToken::LinearGradient || aType == SVGToken::RadialGradient, "SvgGradientNode should only be used for Linear and Radial gradient (!)");
48
0
        }
49
50
        SvgGradientNode::~SvgGradientNode()
51
0
        {
52
            // do NOT delete mpXLink, it's only referenced, not owned
53
0
        }
54
55
        const SvgStyleAttributes* SvgGradientNode::getSvgStyleAttributes() const
56
0
        {
57
0
            return checkForCssStyle(maSvgStyleAttributes);
58
0
        }
59
60
        void SvgGradientNode::parseAttribute(SVGToken aSVGToken, const OUString& aContent)
61
0
        {
62
            // call parent
63
0
            SvgNode::parseAttribute(aSVGToken, aContent);
64
65
            // read style attributes
66
0
            maSvgStyleAttributes.parseStyleAttribute(aSVGToken, aContent);
67
68
            // parse own
69
0
            switch(aSVGToken)
70
0
            {
71
0
                case SVGToken::Style:
72
0
                {
73
0
                    readLocalCssStyle(aContent);
74
0
                    break;
75
0
                }
76
0
                case SVGToken::X1:
77
0
                {
78
0
                    SvgNumber aNum;
79
80
0
                    if(readSingleNumber(aContent, aNum))
81
0
                    {
82
0
                        maX1 = aNum;
83
0
                    }
84
0
                    break;
85
0
                }
86
0
                case SVGToken::Y1:
87
0
                {
88
0
                    SvgNumber aNum;
89
90
0
                    if(readSingleNumber(aContent, aNum))
91
0
                    {
92
0
                        maY1 = aNum;
93
0
                    }
94
0
                    break;
95
0
                }
96
0
                case SVGToken::X2:
97
0
                {
98
0
                    SvgNumber aNum;
99
100
0
                    if(readSingleNumber(aContent, aNum))
101
0
                    {
102
0
                        maX2 = aNum;
103
0
                    }
104
0
                    break;
105
0
                }
106
0
                case SVGToken::Y2:
107
0
                {
108
0
                    SvgNumber aNum;
109
110
0
                    if(readSingleNumber(aContent, aNum))
111
0
                    {
112
0
                        maY2 = aNum;
113
0
                    }
114
0
                    break;
115
0
                }
116
0
                case SVGToken::Cx:
117
0
                {
118
0
                    SvgNumber aNum;
119
120
0
                    if(readSingleNumber(aContent, aNum))
121
0
                    {
122
0
                        maCx = aNum;
123
0
                    }
124
0
                    break;
125
0
                }
126
0
                case SVGToken::Cy:
127
0
                {
128
0
                    SvgNumber aNum;
129
130
0
                    if(readSingleNumber(aContent, aNum))
131
0
                    {
132
0
                        maCy = aNum;
133
0
                    }
134
0
                    break;
135
0
                }
136
0
                case SVGToken::Fx:
137
0
                {
138
0
                    SvgNumber aNum;
139
140
0
                    if(readSingleNumber(aContent, aNum))
141
0
                    {
142
0
                        maFx = aNum;
143
0
                    }
144
0
                    break;
145
0
                }
146
0
                case SVGToken::Fy:
147
0
                {
148
0
                    SvgNumber aNum;
149
150
0
                    if(readSingleNumber(aContent, aNum))
151
0
                    {
152
0
                        maFy = aNum;
153
0
                    }
154
0
                    break;
155
0
                }
156
0
                case SVGToken::R:
157
0
                {
158
0
                    SvgNumber aNum;
159
160
0
                    if(readSingleNumber(aContent, aNum))
161
0
                    {
162
0
                        if(aNum.isPositive())
163
0
                        {
164
0
                            maR = aNum;
165
0
                        }
166
0
                    }
167
0
                    break;
168
0
                }
169
0
                case SVGToken::GradientUnits:
170
0
                {
171
0
                    if(!aContent.isEmpty())
172
0
                    {
173
0
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), commonStrings::aStrUserSpaceOnUse))
174
0
                        {
175
0
                            setGradientUnits(SvgUnits::userSpaceOnUse);
176
0
                        }
177
0
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), commonStrings::aStrObjectBoundingBox))
178
0
                        {
179
0
                            setGradientUnits(SvgUnits::objectBoundingBox);
180
0
                        }
181
0
                    }
182
0
                    break;
183
0
                }
184
0
                case SVGToken::SpreadMethod:
185
0
                {
186
0
                    if(!aContent.isEmpty())
187
0
                    {
188
0
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"pad"))
189
0
                        {
190
0
                            setSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Pad);
191
0
                        }
192
0
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"reflect"))
193
0
                        {
194
0
                            setSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Reflect);
195
0
                        }
196
0
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"repeat"))
197
0
                        {
198
0
                            setSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Repeat);
199
0
                        }
200
0
                    }
201
0
                    break;
202
0
                }
203
0
                case SVGToken::GradientTransform:
204
0
                {
205
0
                    const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this));
206
207
0
                    if(!aMatrix.isIdentity())
208
0
                    {
209
0
                        setGradientTransform(aMatrix);
210
0
                    }
211
0
                    break;
212
0
                }
213
0
                case SVGToken::Href:
214
0
                case SVGToken::XlinkHref:
215
0
                {
216
0
                    readLocalLink(aContent, maXLink);
217
0
                    tryToFindLink();
218
0
                    break;
219
0
                }
220
0
                default:
221
0
                {
222
0
                    break;
223
0
                }
224
0
            }
225
0
        }
226
227
        void SvgGradientNode::collectGradientEntries(drawinglayer::primitive2d::SvgGradientEntryVector& aVector) const
228
0
        {
229
0
            if(getChildren().empty())
230
0
            {
231
0
                const_cast< SvgGradientNode* >(this)->tryToFindLink();
232
233
0
                if (mpXLink && !mbResolvingLink)
234
0
                {
235
0
                    mbResolvingLink = true;
236
0
                    mpXLink->collectGradientEntries(aVector);
237
0
                    mbResolvingLink = false;
238
0
                }
239
0
            }
240
0
            else
241
0
            {
242
0
                const sal_uInt32 nCount(getChildren().size());
243
244
0
                for(sal_uInt32 a(0); a < nCount; a++)
245
0
                {
246
0
                    const SvgGradientStopNode* pCandidate = dynamic_cast< const SvgGradientStopNode* >(getChildren()[a].get());
247
248
0
                    if(pCandidate)
249
0
                    {
250
0
                        const SvgStyleAttributes* pStyle = pCandidate->getSvgStyleAttributes();
251
252
0
                        if(pStyle)
253
0
                        {
254
0
                            const SvgNumber aOffset(pCandidate->getOffset());
255
0
                            double fOffset(0.0);
256
257
0
                            if(SvgUnit::percent == aOffset.getUnit())
258
0
                            {
259
                                // percent is not relative to distances in ColorStop context, solve locally
260
0
                                fOffset = aOffset.getNumber() * 0.01;
261
0
                            }
262
0
                            else
263
0
                            {
264
0
                                fOffset = aOffset.solve(*this);
265
0
                            }
266
267
0
                            if(fOffset < 0.0)
268
0
                            {
269
0
                                OSL_ENSURE(false, "OOps, SvgGradientStopNode with offset out of range (!)");
270
0
                                fOffset = 0.0;
271
0
                            }
272
0
                            else if(fOffset > 1.0)
273
0
                            {
274
0
                                OSL_ENSURE(false, "OOps, SvgGradientStopNode with offset out of range (!)");
275
0
                                fOffset = 1.0;
276
0
                            }
277
278
0
                            aVector.emplace_back(
279
0
                                    fOffset,
280
0
                                    pStyle->getStopColor(),
281
0
                                    pStyle->getStopOpacity().solve(*this));
282
0
                        }
283
0
                        else
284
0
                        {
285
0
                            OSL_ENSURE(false, "OOps, SvgGradientStopNode without Style (!)");
286
0
                        }
287
0
                    }
288
0
                }
289
0
            }
290
0
        }
291
292
        SvgNumber SvgGradientNode::getX1() const
293
0
        {
294
0
            if(maX1.isSet())
295
0
            {
296
0
                return maX1;
297
0
            }
298
299
0
            const_cast< SvgGradientNode* >(this)->tryToFindLink();
300
301
0
            if (mpXLink && !mbResolvingLink)
302
0
            {
303
0
                mbResolvingLink = true;
304
0
                auto ret = mpXLink->getX1();
305
0
                mbResolvingLink = false;
306
0
                return ret;
307
0
            }
308
309
            // default is 0%
310
0
            return SvgNumber(0.0, SvgUnit::percent);
311
0
        }
312
313
        SvgNumber SvgGradientNode::getY1() const
314
0
        {
315
0
            if(maY1.isSet())
316
0
            {
317
0
                return maY1;
318
0
            }
319
320
0
            const_cast< SvgGradientNode* >(this)->tryToFindLink();
321
322
0
            if (mpXLink && !mbResolvingLink)
323
0
            {
324
0
                mbResolvingLink = true;
325
0
                auto ret = mpXLink->getY1();
326
0
                mbResolvingLink = false;
327
0
                return ret;
328
0
            }
329
330
            // default is 0%
331
0
            return SvgNumber(0.0, SvgUnit::percent);
332
0
        }
333
334
        SvgNumber SvgGradientNode::getX2() const
335
0
        {
336
0
            if(maX2.isSet())
337
0
            {
338
0
                return maX2;
339
0
            }
340
341
0
            const_cast< SvgGradientNode* >(this)->tryToFindLink();
342
343
0
            if (mpXLink && !mbResolvingLink)
344
0
            {
345
0
                mbResolvingLink = true;
346
0
                auto ret = mpXLink->getX2();
347
0
                mbResolvingLink = false;
348
0
                return ret;
349
0
            }
350
351
            // default is 100%
352
0
            return SvgNumber(100.0, SvgUnit::percent);
353
0
        }
354
355
        SvgNumber SvgGradientNode::getY2() const
356
0
        {
357
0
            if(maY2.isSet())
358
0
            {
359
0
                return maY2;
360
0
            }
361
362
0
            const_cast< SvgGradientNode* >(this)->tryToFindLink();
363
364
0
            if (mpXLink && !mbResolvingLink)
365
0
            {
366
0
                mbResolvingLink = true;
367
0
                auto ret = mpXLink->getY2();
368
0
                mbResolvingLink = false;
369
0
                return ret;
370
0
            }
371
372
            // default is 0%
373
0
            return SvgNumber(0.0, SvgUnit::percent);
374
0
        }
375
376
        SvgNumber SvgGradientNode::getCx() const
377
0
        {
378
0
            if(maCx.isSet())
379
0
            {
380
0
                return maCx;
381
0
            }
382
383
0
            const_cast< SvgGradientNode* >(this)->tryToFindLink();
384
385
0
            if (mpXLink && !mbResolvingLink)
386
0
            {
387
0
                mbResolvingLink = true;
388
0
                auto ret = mpXLink->getCx();
389
0
                mbResolvingLink = false;
390
0
                return ret;
391
0
            }
392
393
            // default is 50%
394
0
            return SvgNumber(50.0, SvgUnit::percent);
395
0
        }
396
397
        SvgNumber SvgGradientNode::getCy() const
398
0
        {
399
0
            if(maCy.isSet())
400
0
            {
401
0
                return maCy;
402
0
            }
403
404
0
            const_cast< SvgGradientNode* >(this)->tryToFindLink();
405
406
0
            if (mpXLink && !mbResolvingLink)
407
0
            {
408
0
                mbResolvingLink = true;
409
0
                auto ret = mpXLink->getCy();
410
0
                mbResolvingLink = false;
411
0
                return ret;
412
0
            }
413
414
            // default is 50%
415
0
            return SvgNumber(50.0, SvgUnit::percent);
416
0
        }
417
418
        SvgNumber SvgGradientNode::getR() const
419
0
        {
420
0
            if(maR.isSet())
421
0
            {
422
0
                return maR;
423
0
            }
424
425
0
            const_cast< SvgGradientNode* >(this)->tryToFindLink();
426
427
0
            if (mpXLink && !mbResolvingLink)
428
0
            {
429
0
                mbResolvingLink = true;
430
0
                auto ret = mpXLink->getR();
431
0
                mbResolvingLink = false;
432
0
                return ret;
433
0
            }
434
435
            // default is 50%
436
0
            return SvgNumber(50.0, SvgUnit::percent);
437
0
        }
438
439
        const SvgNumber* SvgGradientNode::getFx() const
440
0
        {
441
0
            if(maFx.isSet())
442
0
            {
443
0
                return &maFx;
444
0
            }
445
446
0
            const_cast< SvgGradientNode* >(this)->tryToFindLink();
447
448
0
            if (mpXLink && !mbResolvingLink)
449
0
            {
450
0
                mbResolvingLink = true;
451
0
                auto ret = mpXLink->getFx();
452
0
                mbResolvingLink = false;
453
0
                return ret;
454
0
            }
455
456
0
            return nullptr;
457
0
        }
458
459
        const SvgNumber* SvgGradientNode::getFy() const
460
0
        {
461
0
            if(maFy.isSet())
462
0
            {
463
0
                return &maFy;
464
0
            }
465
466
0
            const_cast< SvgGradientNode* >(this)->tryToFindLink();
467
468
0
            if (mpXLink && !mbResolvingLink)
469
0
            {
470
0
                mbResolvingLink = true;
471
0
                auto ret = mpXLink->getFy();
472
0
                mbResolvingLink = false;
473
0
                return ret;
474
0
            }
475
476
0
            return nullptr;
477
0
        }
478
479
        std::optional<basegfx::B2DHomMatrix> SvgGradientNode::getGradientTransform() const
480
0
        {
481
0
            if(mpaGradientTransform)
482
0
            {
483
0
                return mpaGradientTransform;
484
0
            }
485
486
0
            const_cast< SvgGradientNode* >(this)->tryToFindLink();
487
488
0
            if (mpXLink && !mbResolvingLink)
489
0
            {
490
0
                mbResolvingLink = true;
491
0
                auto ret = mpXLink->getGradientTransform();
492
0
                mbResolvingLink = false;
493
0
                return ret;
494
0
            }
495
496
0
            return std::nullopt;
497
0
        }
498
499
} // end of namespace svgio::svgreader
500
501
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */