Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svgio/source/svgreader/svgnode.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 <svgdocument.hxx>
21
#include <svgnode.hxx>
22
#include <svgstyleattributes.hxx>
23
#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
24
#include <o3tl/string_view.hxx>
25
#include <osl/diagnose.h>
26
#include <tools/urlobj.hxx>
27
28
29
namespace svgio::svgreader
30
{
31
        /// #i125258#
32
        bool SvgNode::supportsParentStyle() const
33
230
        {
34
230
            return true;
35
230
        }
36
37
        const SvgStyleAttributes* SvgNode::getSvgStyleAttributes() const
38
0
        {
39
0
            return nullptr;
40
0
        }
41
42
        void SvgNode::addCssStyle(const SvgDocument& rDocument, const OUString& aConcatenated)
43
0
        {
44
0
            const SvgStyleAttributes* pNew = rDocument.findGlobalCssStyleAttributes(aConcatenated);
45
46
0
            if(pNew)
47
0
            {
48
                // add CssStyle if found
49
0
                maCssStyleVector.push_back(pNew);
50
0
            }
51
0
        }
52
53
namespace {
54
        std::vector< OUString > parseClass(const SvgNode& rNode)
55
0
        {
56
0
            std::vector< OUString > aParts;
57
58
            // check for 'class' references (a list of entries is allowed)
59
0
            if(rNode.getClass())
60
0
            {
61
0
                const OUString& rClassList = *rNode.getClass();
62
0
                const sal_Int32 nLen(rClassList.getLength());
63
64
0
                if(nLen)
65
0
                {
66
0
                    sal_Int32 nPos(0);
67
0
                    OUStringBuffer aToken;
68
69
0
                    while(nPos < nLen)
70
0
                    {
71
0
                        const sal_Int32 nInitPos(nPos);
72
0
                        copyToLimiter(rClassList, u' ', nPos, aToken, nLen);
73
0
                        skip_char(rClassList, u' ', nPos, nLen);
74
0
                        const OUString aPart(o3tl::trim(aToken));
75
0
                        aToken.setLength(0);
76
77
0
                        if(aPart.getLength())
78
0
                        {
79
0
                            aParts.push_back(aPart);
80
0
                        }
81
82
0
                        if(nInitPos == nPos)
83
0
                        {
84
0
                            OSL_ENSURE(false, "Could not interpret on current position (!)");
85
0
                            nPos++;
86
0
                        }
87
0
                    }
88
0
                }
89
0
            }
90
91
0
            return aParts;
92
0
        }
93
} //namespace
94
95
        void SvgNode::fillCssStyleVectorUsingHierarchyAndSelectors(
96
            const SvgNode& rCurrent,
97
            std::u16string_view aConcatenated)
98
115
        {
99
115
            const SvgDocument& rDocument = getDocument();
100
101
115
            if(!rDocument.hasGlobalCssStyleAttributes())
102
115
                return;
103
104
0
            const SvgNode* pParent = rCurrent.getParent();
105
0
            OUString sCurrentType(SVGTokenToStr(rCurrent.getType()));
106
107
            // check for ID (highest priority)
108
0
            if(rCurrent.getId())
109
0
            {
110
0
                const OUString& rId = *rCurrent.getId();
111
112
0
                if(rId.getLength())
113
0
                {
114
0
                    const OUString aNewConcatenated("#" + rId + aConcatenated);
115
0
                    addCssStyle(rDocument, aNewConcatenated);
116
117
0
                    if(!sCurrentType.isEmpty())
118
0
                        addCssStyle(rDocument, sCurrentType + aNewConcatenated);
119
120
0
                    if(pParent)
121
0
                    {
122
                        // check for combined selectors at parent first so that higher specificity will be in front
123
0
                        fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, aNewConcatenated);
124
0
                    }
125
0
                }
126
0
            }
127
128
0
            std::vector <OUString> aClasses = parseClass(rCurrent);
129
0
            for(const auto &aClass : aClasses)
130
0
            {
131
0
                const OUString aNewConcatenated("." + aClass + aConcatenated);
132
0
                addCssStyle(rDocument, aNewConcatenated);
133
134
0
                if(!sCurrentType.isEmpty())
135
0
                    addCssStyle(rDocument, sCurrentType + aNewConcatenated);
136
137
0
                if(pParent)
138
0
                {
139
                    // check for combined selectors at parent first so that higher specificity will be in front
140
0
                    fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, aNewConcatenated);
141
0
                }
142
0
            }
143
144
0
            if(!sCurrentType.isEmpty())
145
0
            {
146
0
                const OUString aNewConcatenated(sCurrentType + aConcatenated);
147
0
                addCssStyle(rDocument, aNewConcatenated);
148
0
            }
149
150
0
            OUString sType(SVGTokenToStr(getType()));
151
152
            // check for class-dependent references to CssStyles
153
0
            if(sType.isEmpty())
154
0
                return;
155
156
0
            if(pParent)
157
0
            {
158
                // check for combined selectors at parent first so that higher specificity will be in front
159
0
                fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, sType);
160
0
            }
161
0
        }
162
163
        void SvgNode::fillCssStyleVectorUsingParent(const SvgNode& rCurrent)
164
115
        {
165
115
            const SvgDocument& rDocument = getDocument();
166
167
115
            if(!rDocument.hasGlobalCssStyleAttributes())
168
115
                return;
169
170
0
            const SvgNode* pParent = rCurrent.getParent();
171
172
0
            if (!pParent)
173
0
                return;
174
175
0
            OUString sParentId;
176
0
            if (pParent->getId().has_value())
177
0
            {
178
0
                sParentId = pParent->getId().value();
179
0
            }
180
0
            std::vector <OUString> aParentClasses = parseClass(*pParent);
181
0
            OUString sParentType(SVGTokenToStr(pParent->getType()));
182
183
0
            if(rCurrent.getId())
184
0
            {
185
0
                const OUString& rId = *rCurrent.getId();
186
187
0
                if(!rId.isEmpty())
188
0
                {
189
0
                    if (!sParentId.isEmpty())
190
0
                    {
191
0
                        const OUString aConcatenated("#" + sParentId + ">#" + rId);
192
0
                        addCssStyle(rDocument, aConcatenated);
193
0
                    }
194
195
0
                    for(const auto &aParentClass : aParentClasses)
196
0
                    {
197
0
                        const OUString aConcatenated("." + aParentClass + ">#" + rId);
198
0
                        addCssStyle(rDocument, aConcatenated);
199
0
                    }
200
201
0
                    if (!sParentType.isEmpty())
202
0
                    {
203
0
                        const OUString aConcatenated(sParentType + ">#" + rId);
204
0
                        addCssStyle(rDocument, aConcatenated);
205
0
                    }
206
0
                }
207
208
0
            }
209
210
0
            std::vector <OUString> aClasses = parseClass(rCurrent);
211
0
            for(const auto &aClass : aClasses)
212
0
            {
213
214
0
                if (!sParentId.isEmpty())
215
0
                {
216
0
                    const OUString aConcatenated("#" + sParentId + ">." + aClass);
217
0
                    addCssStyle(rDocument, aConcatenated);
218
0
                }
219
220
0
                for(const auto &aParentClass : aParentClasses)
221
0
                {
222
0
                    const OUString aConcatenated("." + aParentClass + ">." + aClass);
223
0
                    addCssStyle(rDocument, aConcatenated);
224
0
                }
225
226
0
                if (!sParentType.isEmpty())
227
0
                {
228
0
                    const OUString aConcatenated(sParentType + ">." + aClass);
229
0
                    addCssStyle(rDocument, aConcatenated);
230
0
                }
231
0
            }
232
233
0
            OUString sCurrentType(SVGTokenToStr(getType()));
234
235
0
            if(!sCurrentType.isEmpty())
236
0
            {
237
0
                if (!sParentId.isEmpty())
238
0
                {
239
0
                    const OUString aConcatenated("#" + sParentId + ">" + sCurrentType);
240
0
                    addCssStyle(rDocument, aConcatenated);
241
0
                }
242
243
0
                for(const auto &aParentClass : aParentClasses)
244
0
                {
245
0
                    const OUString aConcatenated("." + aParentClass + ">" + sCurrentType);
246
0
                    addCssStyle(rDocument, aConcatenated);
247
0
                }
248
249
0
                if (!sParentType.isEmpty())
250
0
                {
251
0
                    const OUString aConcatenated(sParentType + ">" + sCurrentType);
252
0
                    addCssStyle(rDocument, aConcatenated);
253
0
                }
254
0
            }
255
0
        }
256
257
        void SvgNode::fillCssStyleVector(const SvgStyleAttributes& rOriginal)
258
115
        {
259
115
            OSL_ENSURE(!mbCssStyleVectorBuilt, "OOps, fillCssStyleVector called double ?!?");
260
115
            mbCssStyleVectorBuilt = true;
261
262
            // #i125293# If we have CssStyles we need to build a linked list of SvgStyleAttributes
263
            // which represent this for the current object. There are various methods to
264
            // specify CssStyles which need to be taken into account in a given order:
265
            // - local CssStyle (independent from global CssStyles at SvgDocument)
266
            // - 'id' CssStyle
267
            // - 'class' CssStyle(s)
268
            // - type-dependent elements (e..g. 'rect' for all rect elements)
269
            // - Css selector '*'
270
            // - local attributes (rOriginal)
271
            // - inherited attributes (up the hierarchy)
272
            // The first four will be collected in maCssStyleVector for the current element
273
            // (once, this will not change) and be linked in the needed order using the
274
            // get/setCssStyle at the SvgStyleAttributes which will be used preferred in
275
            // member evaluation over the existing parent hierarchy
276
277
            // check for local CssStyle with highest priority
278
115
            if(mpLocalCssStyle)
279
0
            {
280
                // if we have one, use as first entry
281
0
                maCssStyleVector.push_back(mpLocalCssStyle.get());
282
0
            }
283
284
            // tdf#156038 check for child combinator
285
115
            fillCssStyleVectorUsingParent(*this);
286
287
            // check the hierarchy for concatenated patterns of Selectors
288
115
            fillCssStyleVectorUsingHierarchyAndSelectors(*this, std::u16string_view());
289
290
291
            // tdf#99115, Add css selector '*' style only if the element is on top of the hierarchy
292
            // meaning its parent is <svg>
293
115
            const SvgNode* pParent = this->getParent();
294
295
115
            if(pParent && pParent->getType() == SVGToken::Svg)
296
0
            {
297
                // #i125329# find Css selector '*', add as last element if found
298
0
                const SvgStyleAttributes* pNew = getDocument().findGlobalCssStyleAttributes(u"*"_ustr);
299
300
0
                if(pNew)
301
0
                {
302
                    // add CssStyle for selector '*' if found
303
0
                    maCssStyleVector.push_back(pNew);
304
0
                }
305
0
            }
306
307
            //local attributes
308
115
            maCssStyleVector.push_back(&rOriginal);
309
115
        }
310
311
        const SvgStyleAttributes* SvgNode::checkForCssStyle(const SvgStyleAttributes& rOriginal) const
312
115
        {
313
115
            if(!mbCssStyleVectorBuilt)
314
115
            {
315
                // build needed CssStyleVector for local node
316
115
                const_cast< SvgNode* >(this)->fillCssStyleVector(rOriginal);
317
115
            }
318
319
115
            if(maCssStyleVector.empty())
320
0
            {
321
                // return given original if no CssStyles found
322
0
                return &rOriginal;
323
0
            }
324
115
            else
325
115
            {
326
                // #i125293# rOriginal will be the last element in the linked list; use no CssStyleParent
327
                // there (reset it) to ensure that the parent hierarchy will be used when it's base
328
                // is referenced. This new chaining inserts the CssStyles before the original style,
329
                // this makes the whole process much safer since the original style when used will
330
                // be not different to the situation without CssStyles; thus loops which may be caused
331
                // by trying to use the parent hierarchy of the owner of the style will be avoided
332
                // already in this mechanism. It's still good to keep the supportsParentStyle
333
                // from #i125258# in place, though.
334
                // This chain building using pointers will be done every time when checkForCssStyle
335
                // is used (not the search, only the chaining). This is needed since the CssStyles
336
                // themselves will be potentially used multiple times. It is not expensive since it's
337
                // only changing some pointers.
338
                // The alternative would be to create the style hierarchy for every element (or even
339
                // for the element containing the hierarchy) in a vector of pointers and to use that.
340
                // Resetting the CssStyleParent on rOriginal is probably not needed
341
                // but simply safer to do.
342
343
                // loop over the existing CssStyles and link them. There is a first one, take
344
                // as current
345
115
                SvgStyleAttributes* pCurrent = const_cast< SvgStyleAttributes* >(maCssStyleVector[0]);
346
347
115
                for(size_t a(1); a < maCssStyleVector.size(); a++)
348
0
                {
349
0
                    SvgStyleAttributes* pNext = const_cast< SvgStyleAttributes* >(maCssStyleVector[a]);
350
351
0
                    pCurrent->setCssStyle(pNext);
352
0
                    pCurrent = pNext;
353
0
                }
354
355
                // return 1st CssStyle as style chain start element (only for the
356
                // local element, still no hierarchy used here)
357
115
                return maCssStyleVector[0];
358
115
            }
359
115
        }
360
361
        SvgNode::SvgNode(
362
            SVGToken aType,
363
            SvgDocument& rDocument,
364
            SvgNode* pParent)
365
17.2k
        :   maType(aType),
366
17.2k
            mrDocument(rDocument),
367
17.2k
            mpParent(pParent),
368
17.2k
            mpAlternativeParent(nullptr),
369
17.2k
            maXmlSpace(XmlSpace::NotSet),
370
17.2k
            maDisplay(maType == SVGToken::Unknown ? Display::None : Display::Inline), // tdf#150124: do not display unknown nodes
371
17.2k
            mbDecomposing(false),
372
17.2k
            mbCssStyleVectorBuilt(false)
373
17.2k
        {
374
17.2k
            if (pParent)
375
6.32k
            {
376
6.32k
                pParent->maChildren.emplace_back(this);
377
6.32k
            }
378
17.2k
        }
379
380
        SvgNode::~SvgNode()
381
17.2k
        {
382
17.2k
        }
383
384
        void SvgNode::readLocalCssStyle(std::u16string_view aContent)
385
0
        {
386
0
            if(!mpLocalCssStyle)
387
0
            {
388
                // create LocalCssStyle if needed but not yet added
389
0
                mpLocalCssStyle.reset(new SvgStyleAttributes(*this));
390
0
            }
391
0
            else
392
0
            {
393
                // 2nd fill would be an error
394
0
                OSL_ENSURE(false, "Svg node has two local CssStyles, this may lead to problems (!)");
395
0
            }
396
397
0
            if(mpLocalCssStyle)
398
0
            {
399
                // parse and set values to it
400
0
                mpLocalCssStyle->readCssStyle(aContent);
401
0
            }
402
0
            else
403
0
            {
404
0
                OSL_ENSURE(false, "Could not get/create a local CssStyle for a node (!)");
405
0
            }
406
0
        }
407
408
        void SvgNode::parseAttributes(const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs)
409
11.8k
        {
410
            // no longer need to pre-sort moving 'style' entries to the back so that
411
            // values get overwritten - that was the previous, not complete solution for
412
            // handling the priorities between svg and Css properties
413
11.8k
            const sal_uInt32 nAttributes(xAttribs->getLength());
414
415
11.8k
            for(sal_uInt32 a(0); a < nAttributes; a++)
416
0
            {
417
0
                const OUString aTokenName(xAttribs->getNameByIndex(a));
418
0
                const SVGToken aSVGToken(StrToSVGToken(aTokenName, false));
419
420
0
                parseAttribute(aSVGToken, xAttribs->getValueByIndex(a));
421
0
            }
422
11.8k
        }
423
424
        Display getDisplayFromContent(std::u16string_view aContent)
425
0
        {
426
0
            if(!aContent.empty())
427
0
            {
428
0
                if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"inline"))
429
0
                {
430
0
                    return Display::Inline;
431
0
                }
432
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"none"))
433
0
                {
434
0
                    return Display::None;
435
0
                }
436
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"inherit"))
437
0
                {
438
0
                    return Display::Inherit;
439
0
                }
440
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"block"))
441
0
                {
442
0
                    return Display::Block;
443
0
                }
444
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"list-item"))
445
0
                {
446
0
                    return Display::ListItem;
447
0
                }
448
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"run-in"))
449
0
                {
450
0
                    return Display::RunIn;
451
0
                }
452
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"compact"))
453
0
                {
454
0
                    return Display::Compact;
455
0
                }
456
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"marker"))
457
0
                {
458
0
                    return Display::Marker;
459
0
                }
460
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"table"))
461
0
                {
462
0
                    return Display::Table;
463
0
                }
464
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"inline-table"))
465
0
                {
466
0
                    return Display::InlineTable;
467
0
                }
468
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"table-row-group"))
469
0
                {
470
0
                    return Display::TableRowGroup;
471
0
                }
472
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"table-header-group"))
473
0
                {
474
0
                    return Display::TableHeaderGroup;
475
0
                }
476
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"table-footer-group"))
477
0
                {
478
0
                    return Display::TableFooterGroup;
479
0
                }
480
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"table-row"))
481
0
                {
482
0
                    return Display::TableRow;
483
0
                }
484
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"table-column-group"))
485
0
                {
486
0
                    return Display::TableColumnGroup;
487
0
                }
488
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"table-column"))
489
0
                {
490
0
                    return Display::TableColumn;
491
0
                }
492
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"table-cell"))
493
0
                {
494
0
                    return Display::TableCell;
495
0
                }
496
0
                else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"table-caption"))
497
0
                {
498
0
                    return Display::TableCaption;
499
0
                }
500
0
            }
501
502
            // return the default
503
0
            return Display::Inline;
504
0
        }
505
506
        void SvgNode::parseAttribute(SVGToken aSVGToken, const OUString& aContent)
507
8
        {
508
8
            switch(aSVGToken)
509
8
            {
510
0
                case SVGToken::Id:
511
0
                {
512
0
                    if(!aContent.isEmpty())
513
0
                    {
514
0
                        setId(aContent);
515
0
                    }
516
0
                    break;
517
0
                }
518
0
                case SVGToken::Class:
519
0
                {
520
0
                    if(!aContent.isEmpty())
521
0
                    {
522
0
                        setClass(aContent);
523
0
                    }
524
0
                    break;
525
0
                }
526
0
                case SVGToken::SystemLanguage:
527
0
                {
528
0
                    SvgStringVector aSvgStringVector;
529
530
0
                    if (readSvgStringVector(aContent, aSvgStringVector, ','))
531
0
                        maSystemLanguage = std::move(aSvgStringVector);
532
0
                    break;
533
0
                }
534
0
                case SVGToken::XmlSpace:
535
0
                {
536
0
                    if(!aContent.isEmpty())
537
0
                    {
538
0
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"default"))
539
0
                        {
540
0
                            setXmlSpace(XmlSpace::Default);
541
0
                        }
542
0
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"preserve"))
543
0
                        {
544
0
                            setXmlSpace(XmlSpace::Preserve);
545
0
                        }
546
0
                    }
547
0
                    break;
548
0
                }
549
0
                case SVGToken::Display:
550
0
                {
551
0
                    if(!aContent.isEmpty())
552
0
                    {
553
0
                        setDisplay(getDisplayFromContent(aContent));
554
0
                    }
555
0
                    break;
556
0
                }
557
8
                default:
558
8
                {
559
8
                    break;
560
0
                }
561
8
            }
562
8
        }
563
564
        void SvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DContainer& rTarget, bool bReferenced) const
565
116
        {
566
116
            if (mbDecomposing) //guard against infinite recurse
567
0
                return;
568
569
116
            if(Display::None == getDisplay())
570
0
            {
571
0
                return;
572
0
            }
573
574
116
            if(!bReferenced)
575
116
            {
576
116
                if(SVGToken::Defs == getType() ||
577
116
                    SVGToken::Symbol == getType() ||
578
116
                    SVGToken::ClipPathNode == getType() ||
579
116
                    SVGToken::Mask == getType() ||
580
116
                    SVGToken::Marker == getType() ||
581
116
                    SVGToken::Pattern == getType())
582
0
                {
583
                    // do not decompose defs or symbol nodes (these hold only style-like
584
                    // objects which may be used by referencing them) except when doing
585
                    // so controlled referenced
586
587
                    // also do not decompose ClipPaths and Masks. These should be embedded
588
                    // in a defs node (which gets not decomposed by itself), but you never
589
                    // know
590
591
                    // also not directly used are Markers and Patterns, only indirectly used
592
                    // by reference
593
594
                    // #i121656# also do not decompose nodes which have display="none" set
595
                    // as property
596
0
                    return;
597
0
                }
598
116
            }
599
600
116
            const auto& rChildren = getChildren();
601
602
116
            if(rChildren.empty())
603
116
                return;
604
605
0
            mbDecomposing = true;
606
607
0
            const sal_uInt32 nCount(rChildren.size());
608
609
0
            for(sal_uInt32 a(0); a < nCount; a++)
610
0
            {
611
0
                SvgNode* pCandidate = rChildren[a].get();
612
613
0
                if(pCandidate && Display::None != pCandidate->getDisplay())
614
0
                {
615
0
                    const auto& rGrandChildren = pCandidate->getChildren();
616
0
                    const SvgStyleAttributes* pChildStyles = pCandidate->getSvgStyleAttributes();
617
                    // decompose:
618
                    // - visible terminal nodes
619
                    // - all non-terminal nodes (might contain visible nodes down the hierarchy)
620
0
                    if( !rGrandChildren.empty() || ( pChildStyles && (Visibility::visible == pChildStyles->getVisibility())) )
621
0
                    {
622
0
                        drawinglayer::primitive2d::Primitive2DContainer aNewTarget;
623
0
                        pCandidate->decomposeSvgNode(aNewTarget, bReferenced);
624
625
0
                        if(!aNewTarget.empty())
626
0
                        {
627
0
                            rTarget.append(aNewTarget);
628
0
                        }
629
0
                    }
630
0
                }
631
0
                else if(!pCandidate)
632
0
                {
633
0
                    OSL_ENSURE(false, "Null-Pointer in child node list (!)");
634
0
                }
635
0
            }
636
637
0
            if(!rTarget.empty())
638
0
            {
639
0
                const SvgStyleAttributes* pStyles = getSvgStyleAttributes();
640
0
                if(pStyles)
641
0
                {
642
                    // check if we have Title or Desc
643
0
                    const OUString& rTitle = pStyles->getTitle();
644
0
                    const OUString& rDesc = pStyles->getDesc();
645
646
0
                    if(!rTitle.isEmpty() || !rDesc.isEmpty())
647
0
                    {
648
                        // default object name is empty
649
0
                        OUString aObjectName;
650
651
                        // use path as object name when outmost element
652
0
                        if (SVGToken::Svg == getType())
653
0
                        {
654
0
                            aObjectName = getDocument().getAbsolutePath();
655
656
0
                            if(!aObjectName.isEmpty())
657
0
                            {
658
0
                                INetURLObject aURL(aObjectName);
659
660
0
                                aObjectName = aURL.getName(
661
0
                                    INetURLObject::LAST_SEGMENT,
662
0
                                    true,
663
0
                                    INetURLObject::DecodeMechanism::WithCharset);
664
0
                            }
665
0
                        }
666
667
0
                        rTarget = drawinglayer::primitive2d::Primitive2DContainer {
668
                            // pack in ObjectInfoPrimitive2D group
669
0
                                new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
670
0
                                    std::move(rTarget),
671
0
                                    aObjectName,
672
0
                                    rTitle,
673
0
                                    rDesc)
674
0
                        };
675
0
                    }
676
0
                }
677
0
            }
678
0
            mbDecomposing = false;
679
0
        }
680
681
        basegfx::B2DRange SvgNode::getCurrentViewPort() const
682
0
        {
683
0
            if(getParent())
684
0
            {
685
0
                return getParent()->getCurrentViewPort();
686
0
            }
687
0
            else
688
0
            {
689
0
                return basegfx::B2DRange(); // return empty B2DRange
690
0
            }
691
0
        }
692
693
        double SvgNode::getCurrentFontSize() const
694
0
        {
695
0
            if(getSvgStyleAttributes())
696
0
                return getSvgStyleAttributes()->getFontSizeNumber().solve(*this, NumberType::xcoordinate);
697
698
0
            if(getParent())
699
0
                return getParent()->getCurrentFontSize();
700
701
0
            return 0.0;
702
0
        }
703
704
        double SvgNode::getCurrentXHeight() const
705
0
        {
706
            // https://drafts.csswg.org/css-values-4/#ex
707
            // for XHeight, use 0.5em fallback currently
708
            // FIXME: use "x-height of the first available font"
709
0
            return getCurrentFontSize() * 0.5;
710
0
        }
711
712
        void SvgNode::setId(OUString const & rId)
713
0
        {
714
0
            if(mpId)
715
0
            {
716
0
                mrDocument.removeSvgNodeFromMapper(*mpId);
717
0
                mpId.reset();
718
0
            }
719
720
0
            mpId = rId;
721
0
            mrDocument.addSvgNodeToMapper(*mpId, *this);
722
0
        }
723
724
        void SvgNode::setClass(OUString const & rClass)
725
0
        {
726
0
            if(mpClass)
727
0
            {
728
0
                mrDocument.removeSvgNodeFromMapper(*mpClass);
729
0
                mpClass.reset();
730
0
            }
731
732
0
            mpClass = rClass;
733
0
            mrDocument.addSvgNodeToMapper(*mpClass, *this);
734
0
        }
735
736
        XmlSpace SvgNode::getXmlSpace() const
737
0
        {
738
0
            if(maXmlSpace != XmlSpace::NotSet)
739
0
            {
740
0
                return maXmlSpace;
741
0
            }
742
743
0
            if(getParent())
744
0
            {
745
0
                return getParent()->getXmlSpace();
746
0
            }
747
748
            // default is XmlSpace::Default
749
0
            return XmlSpace::Default;
750
0
        }
751
752
        void SvgNode::accept(Visitor & rVisitor)
753
0
        {
754
0
            rVisitor.visit(*this);
755
0
        }
756
} // end of namespace svgio
757
758
759
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */