Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svgio/source/svgreader/svgsymbolnode.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 <svgsymbolnode.hxx>
21
#include <basegfx/matrix/b2dhommatrix.hxx>
22
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
23
24
namespace svgio::svgreader
25
{
26
        SvgSymbolNode::SvgSymbolNode(
27
            SvgDocument& rDocument,
28
            SvgNode* pParent)
29
0
        :   SvgNode(SVGToken::Symbol, rDocument, pParent),
30
0
            maSvgStyleAttributes(*this)
31
0
        {
32
0
        }
33
34
        SvgSymbolNode::~SvgSymbolNode()
35
0
        {
36
0
        }
37
38
        const SvgStyleAttributes* SvgSymbolNode::getSvgStyleAttributes() const
39
0
        {
40
0
            return &maSvgStyleAttributes;
41
0
        }
42
43
        void SvgSymbolNode::parseAttribute(SVGToken aSVGToken, const OUString& aContent)
44
0
        {
45
            // call parent
46
0
            SvgNode::parseAttribute(aSVGToken, aContent);
47
48
            // read style attributes
49
0
            maSvgStyleAttributes.parseStyleAttribute(aSVGToken, aContent);
50
51
            // parse own
52
0
            switch(aSVGToken)
53
0
            {
54
0
                case SVGToken::Style:
55
0
                {
56
0
                    readLocalCssStyle(aContent);
57
0
                    break;
58
0
                }
59
0
                case SVGToken::X:
60
0
                {
61
0
                    SvgNumber aNum;
62
63
0
                    if(readSingleNumber(aContent, aNum))
64
0
                    {
65
0
                        maX = aNum;
66
0
                    }
67
0
                    break;
68
0
                }
69
0
                case SVGToken::Y:
70
0
                {
71
0
                    SvgNumber aNum;
72
73
0
                    if(readSingleNumber(aContent, aNum))
74
0
                    {
75
0
                        maY = aNum;
76
0
                    }
77
0
                    break;
78
0
                }
79
0
                case SVGToken::Width:
80
0
                {
81
0
                    SvgNumber aNum;
82
83
0
                    if(readSingleNumber(aContent, aNum))
84
0
                    {
85
0
                        if(aNum.isPositive())
86
0
                        {
87
0
                            maWidth = aNum;
88
0
                        }
89
0
                    }
90
0
                    break;
91
0
                }
92
0
                case SVGToken::Height:
93
0
                {
94
0
                    SvgNumber aNum;
95
96
0
                    if(readSingleNumber(aContent, aNum))
97
0
                    {
98
0
                        if(aNum.isPositive())
99
0
                        {
100
0
                            maHeight = aNum;
101
0
                        }
102
0
                    }
103
0
                    break;
104
0
                }
105
0
                case SVGToken::ViewBox:
106
0
                {
107
0
                    const basegfx::B2DRange aRange(readViewBox(aContent, *this));
108
109
0
                    if(!aRange.isEmpty())
110
0
                    {
111
0
                        setViewBox(&aRange);
112
0
                    }
113
0
                    break;
114
0
                }
115
0
                case SVGToken::PreserveAspectRatio:
116
0
                {
117
0
                    maSvgAspectRatio = readSvgAspectRatio(aContent);
118
0
                    break;
119
0
                }
120
0
                default:
121
0
                {
122
0
                    break;
123
0
                }
124
0
            }
125
0
        }
126
127
        void SvgSymbolNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DContainer& rTarget, bool bReferenced) const
128
0
        {
129
            // decompose children
130
0
            drawinglayer::primitive2d::Primitive2DContainer aContent;
131
0
            SvgNode::decomposeSvgNode(aContent, bReferenced);
132
133
            // no geometry provided, done
134
0
            if (aContent.empty())
135
0
                return;
136
137
            // if no ViewBox append aContent without embedding
138
0
            if(nullptr == getViewBox())
139
0
            {
140
0
                rTarget.append(aContent);
141
0
                return;
142
0
            }
143
144
            // prepare range of imported geometry
145
            // tdf#164434 CAUTION: There *are* svg files which do not define
146
            // all needed data, e.g. x/y/w/h might be missing in any combination.
147
            // As fallback, use size of imported geometry. Do this separate for
148
            // position and size to do the best if only one value pair is provided
149
0
            basegfx::B2DRange aImportedRange;
150
0
            basegfx::B2DPoint aPosition;
151
0
            basegfx::B2DVector aSize;
152
153
            // evaluate position
154
0
            if (maX.isSet() && maY.isSet())
155
0
            {
156
                // use values from imported svg if both provided
157
0
                aPosition = basegfx::B2DPoint(
158
0
                    maX.solve(*this, NumberType::xcoordinate),
159
0
                    maY.solve(*this, NumberType::ycoordinate));
160
0
            }
161
0
            else
162
0
            {
163
                // fallback to imported geometry
164
0
                drawinglayer::geometry::ViewInformation2D aViewInfo;
165
0
                aImportedRange = aContent.getB2DRange(aViewInfo);
166
0
                aPosition = aImportedRange.getMinimum();
167
0
            }
168
169
            // evaluate size
170
0
            if (maWidth.isSet() && maHeight.isSet())
171
0
            {
172
                // use values from imported svg if both provided
173
0
                aSize = basegfx::B2DVector(
174
0
                    maWidth.solve(*this, NumberType::xcoordinate),
175
0
                    maHeight.solve(*this, NumberType::ycoordinate));
176
0
            }
177
0
            else
178
0
            {
179
                // fallback to imported geometry
180
0
                if (aImportedRange.isEmpty())
181
0
                {
182
0
                    drawinglayer::geometry::ViewInformation2D aViewInfo;
183
0
                    aImportedRange = aContent.getB2DRange(aViewInfo);
184
0
                }
185
186
0
                aSize = aImportedRange.getRange();
187
0
            }
188
189
            // tdf#164434 no size, no geometry, done
190
0
            if (0.0 == aSize.getX() || 0.0 == aSize.getY())
191
0
                return;
192
193
            // create mapping using SvgAspectRatio
194
0
            const SvgAspectRatio& rRatio = getSvgAspectRatio();
195
0
            const basegfx::B2DHomMatrix aEmbeddingTransform(
196
0
                rRatio.createMapping(basegfx::B2DRange(aPosition, aPosition + aSize), *getViewBox()));
197
198
            // prepare embedding in transformation
199
            // create embedding group element with transformation
200
0
            const drawinglayer::primitive2d::Primitive2DReference xRef(
201
0
                new drawinglayer::primitive2d::TransformPrimitive2D(
202
0
                    aEmbeddingTransform,
203
0
                    drawinglayer::primitive2d::Primitive2DContainer(std::move(aContent))));
204
205
            // add embedded geometry to result
206
0
            rTarget.push_back(xRef);
207
0
        }
208
209
} // end of namespace svgio::svgreader
210
211
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */