Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svgio/source/svgreader/svgdocumenthandler.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 <svgdocumenthandler.hxx>
21
#include <svgtoken.hxx>
22
#include <svgsvgnode.hxx>
23
#include <svggnode.hxx>
24
#include <svganode.hxx>
25
#include <svgnode.hxx>
26
#include <svgpathnode.hxx>
27
#include <svgrectnode.hxx>
28
#include <svggradientnode.hxx>
29
#include <svggradientstopnode.hxx>
30
#include <svgswitchnode.hxx>
31
#include <svgsymbolnode.hxx>
32
#include <svgusenode.hxx>
33
#include <svgcirclenode.hxx>
34
#include <svgellipsenode.hxx>
35
#include <svglinenode.hxx>
36
#include <svgpolynode.hxx>
37
#include <svgtextnode.hxx>
38
#include <svgcharacternode.hxx>
39
#include <svgtspannode.hxx>
40
#include <svgtrefnode.hxx>
41
#include <svgtextpathnode.hxx>
42
#include <svgstylenode.hxx>
43
#include <svgimagenode.hxx>
44
#include <svgclippathnode.hxx>
45
#include <svgfeblendnode.hxx>
46
#include <svgfecolormatrixnode.hxx>
47
#include <svgfecompositenode.hxx>
48
#include <svgfedropshadownode.hxx>
49
#include <svgfefloodnode.hxx>
50
#include <svgfeimagenode.hxx>
51
#include <svgfegaussianblurnode.hxx>
52
#include <svgfemergenode.hxx>
53
#include <svgfemergenodenode.hxx>
54
#include <svgfeoffsetnode.hxx>
55
#include <svgfilternode.hxx>
56
#include <svgmasknode.hxx>
57
#include <svgmarkernode.hxx>
58
#include <svgpatternnode.hxx>
59
#include <svgtitledescnode.hxx>
60
#include <sal/log.hxx>
61
#include <osl/diagnose.h>
62
63
#include <com/sun/star/lang/Locale.hpp>
64
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
65
66
using namespace com::sun::star;
67
68
namespace svgio::svgreader
69
{
70
71
namespace
72
{
73
using CharacterNodeHandlerFunc
74
    = svgio::svgreader::SvgCharacterNode*(svgio::svgreader::SvgCharacterNode* pCharNode,
75
                                          svgio::svgreader::SvgTspanNode* pParentLine,
76
                                          svgio::svgreader::SvgCharacterNode* pLast);
77
    // clean whitespace in text span
78
    svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgCharacterNode* pCharNode,
79
                                                           svgio::svgreader::SvgTspanNode* pParentLine,
80
                                                           svgio::svgreader::SvgCharacterNode* pLast)
81
0
    {
82
0
        pCharNode->setParentLine(pParentLine);
83
0
        return pCharNode->whiteSpaceHandling(pLast);
84
0
    }
85
86
    // set correct widths of text lines
87
    svgio::svgreader::SvgCharacterNode* calcTextLineWidths(svgio::svgreader::SvgCharacterNode* pCharNode,
88
                                                           svgio::svgreader::SvgTspanNode* pParentLine,
89
                                                           svgio::svgreader::SvgCharacterNode* /*pLast*/)
90
0
    {
91
0
        if (const SvgStyleAttributes* pSvgStyleAttributes = pCharNode->getSvgStyleAttributes())
92
0
        {
93
0
            const drawinglayer::attribute::FontAttribute aFontAttribute(
94
0
                svgio::svgreader::SvgCharacterNode::getFontAttribute(*pSvgStyleAttributes));
95
96
0
            double fFontWidth(pSvgStyleAttributes->getFontSizeNumber().solve(*pCharNode));
97
0
            double fFontHeight(fFontWidth);
98
99
0
            css::lang::Locale aLocale;
100
0
            drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
101
0
            aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale);
102
0
            double fTextWidth = aTextLayouterDevice.getTextWidth(pCharNode->getText(), 0.0,
103
0
                                                                 pCharNode->getText().getLength());
104
0
            pParentLine->concatenateTextLineWidth(fTextWidth);
105
0
        }
106
0
        return nullptr; // no pLast handling
107
0
    }
108
109
    svgio::svgreader::SvgCharacterNode* walkRecursive(svgio::svgreader::SvgNode const* pNode,
110
                                                      svgio::svgreader::SvgTspanNode* pParentLine,
111
                                                      svgio::svgreader::SvgCharacterNode* pLast,
112
                                                      CharacterNodeHandlerFunc* pHandlerFunc)
113
0
    {
114
0
        if(pNode)
115
0
        {
116
0
            const auto& rChilds = pNode->getChildren();
117
0
            const sal_uInt32 nCount(rChilds.size());
118
119
0
            for(sal_uInt32 a(0); a < nCount; a++)
120
0
            {
121
0
                svgio::svgreader::SvgNode* pCandidate = rChilds[a].get();
122
123
0
                if(pCandidate)
124
0
                {
125
0
                    switch(pCandidate->getType())
126
0
                    {
127
0
                        case SVGToken::Character:
128
0
                        {
129
0
                            svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);
130
131
0
                            pLast = pHandlerFunc(pCharNode, pParentLine, pLast);
132
0
                            break;
133
0
                        }
134
0
                        case SVGToken::Tspan:
135
0
                        {
136
0
                            svgio::svgreader::SvgTspanNode* pTspanNode = static_cast< svgio::svgreader::SvgTspanNode* >(pCandidate);
137
138
                            // If x or y exist it means it's a new line of text
139
0
                            if(!pTspanNode->getX().empty() || !pTspanNode->getY().empty())
140
0
                                pParentLine = pTspanNode;
141
142
                            // recursively handle subhierarchy
143
0
                            pLast = walkRecursive(pCandidate, pParentLine, pLast, pHandlerFunc);
144
0
                            break;
145
0
                        }
146
0
                        case SVGToken::TextPath:
147
0
                        case SVGToken::Tref:
148
0
                        {
149
                            // recursively handle subhierarchy
150
0
                            pLast = walkRecursive(pCandidate, pParentLine, pLast, pHandlerFunc);
151
0
                            break;
152
0
                        }
153
0
                        case SVGToken::Desc:
154
0
                            break;
155
0
                        default:
156
0
                        {
157
0
                            SAL_WARN("svgio", "Unexpected token inside SVGTokenText, SVGToken=" << static_cast<int>(pCandidate->getType()));
158
0
                            break;
159
0
                        }
160
0
                    }
161
0
                }
162
0
            }
163
0
        }
164
165
0
        return pLast;
166
0
    }
167
168
} // end anonymous namespace
169
170
        SvgDocHdl::SvgDocHdl(const OUString& aAbsolutePath)
171
18.8k
        :   maDocument(aAbsolutePath),
172
18.8k
            mpTarget(nullptr)
173
18.8k
        {
174
18.8k
        }
175
176
        SvgDocHdl::~SvgDocHdl()
177
18.8k
        {
178
18.8k
            if (mpTarget)
179
17.7k
            {
180
17.7k
                OSL_ENSURE(false, "SvgDocHdl destructed with active target (!)");
181
182
27.5k
                while (mpTarget->getParent())
183
9.83k
                    mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
184
185
17.7k
                const SvgNodeVector& rOwnedTopLevels = maDocument.getSvgNodeVector();
186
17.7k
                if (std::none_of(rOwnedTopLevels.begin(), rOwnedTopLevels.end(),
187
17.7k
                                [&](std::unique_ptr<SvgNode> const & p) { return p.get() == mpTarget; }))
188
17.7k
                    delete mpTarget;
189
17.7k
            }
190
18.8k
            OSL_ENSURE(maCssContents.empty(), "SvgDocHdl destructed with active css style stack entry (!)");
191
18.8k
        }
192
193
        void SvgDocHdl::startDocument(  )
194
18.8k
        {
195
18.8k
            OSL_ENSURE(!mpTarget, "Already a target at document start (!)");
196
18.8k
            OSL_ENSURE(maCssContents.empty(), "SvgDocHdl startDocument with active css style stack entry (!)");
197
18.8k
        }
198
199
        void SvgDocHdl::endDocument(  )
200
0
        {
201
0
            OSL_ENSURE(!mpTarget, "Still a target at document end (!)");
202
0
            OSL_ENSURE(maCssContents.empty(), "SvgDocHdl endDocument with active css style stack entry (!)");
203
0
        }
204
205
        void SvgDocHdl::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs )
206
29.1k
        {
207
29.1k
            if(aName.isEmpty())
208
0
                return;
209
210
29.1k
            const SVGToken aSVGToken(StrToSVGToken(aName, false));
211
212
29.1k
            switch (aSVGToken)
213
29.1k
            {
214
                /// structural elements
215
0
                case SVGToken::Symbol:
216
0
                {
217
                    /// new basic node for Symbol. Content gets scanned, but
218
                    /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
219
0
                    mpTarget = new SvgSymbolNode(maDocument, mpTarget);
220
0
                    mpTarget->parseAttributes(xAttribs);
221
0
                    break;
222
0
                }
223
0
                case SVGToken::Switch:
224
0
                {
225
                    /// new node for Switch
226
0
                    mpTarget = new SvgSwitchNode(maDocument, mpTarget);
227
0
                    mpTarget->parseAttributes(xAttribs);
228
0
                    break;
229
0
                }
230
0
                case SVGToken::Defs:
231
4
                case SVGToken::G:
232
4
                {
233
                    /// new node for Defs/G
234
4
                    mpTarget = new SvgGNode(aSVGToken, maDocument, mpTarget);
235
4
                    mpTarget->parseAttributes(xAttribs);
236
4
                    break;
237
0
                }
238
18.3k
                case SVGToken::Svg:
239
18.3k
                {
240
                    /// new node for Svg
241
18.3k
                    mpTarget = new SvgSvgNode(maDocument, mpTarget);
242
18.3k
                    mpTarget->parseAttributes(xAttribs);
243
18.3k
                    break;
244
0
                }
245
0
                case SVGToken::Use:
246
0
                {
247
                    /// new node for Use
248
0
                    mpTarget = new SvgUseNode(maDocument, mpTarget);
249
0
                    mpTarget->parseAttributes(xAttribs);
250
0
                    break;
251
0
                }
252
5
                case SVGToken::A:
253
5
                {
254
                    /// new node for A
255
5
                    mpTarget = new SvgANode(maDocument, mpTarget);
256
5
                    mpTarget->parseAttributes(xAttribs);
257
5
                    break;
258
0
                }
259
260
                /// shape elements
261
0
                case SVGToken::Circle:
262
0
                {
263
                    /// new node for Circle
264
0
                    mpTarget = new SvgCircleNode(maDocument, mpTarget);
265
0
                    mpTarget->parseAttributes(xAttribs);
266
0
                    break;
267
0
                }
268
0
                case SVGToken::Ellipse:
269
0
                {
270
                    /// new node for Ellipse
271
0
                    mpTarget = new SvgEllipseNode(maDocument, mpTarget);
272
0
                    mpTarget->parseAttributes(xAttribs);
273
0
                    break;
274
0
                }
275
0
                case SVGToken::Line:
276
0
                {
277
                    /// new node for Line
278
0
                    mpTarget = new SvgLineNode(maDocument, mpTarget);
279
0
                    mpTarget->parseAttributes(xAttribs);
280
0
                    break;
281
0
                }
282
0
                case SVGToken::Path:
283
0
                {
284
                    /// new node for Path
285
0
                    mpTarget = new SvgPathNode(maDocument, mpTarget);
286
0
                    mpTarget->parseAttributes(xAttribs);
287
0
                    break;
288
0
                }
289
0
                case SVGToken::Polygon:
290
0
                {
291
                    /// new node for Polygon
292
0
                    mpTarget = new SvgPolyNode(aSVGToken, maDocument, mpTarget);
293
0
                    mpTarget->parseAttributes(xAttribs);
294
0
                    break;
295
0
                }
296
0
                case SVGToken::Polyline:
297
0
                {
298
                    /// new node for Polyline
299
0
                    mpTarget = new SvgPolyNode(aSVGToken, maDocument, mpTarget);
300
0
                    mpTarget->parseAttributes(xAttribs);
301
0
                    break;
302
0
                }
303
0
                case SVGToken::Rect:
304
0
                {
305
                    /// new node for Rect
306
0
                    mpTarget = new SvgRectNode(maDocument, mpTarget);
307
0
                    mpTarget->parseAttributes(xAttribs);
308
0
                    break;
309
0
                }
310
1
                case SVGToken::Image:
311
1
                {
312
                    /// new node for Image
313
1
                    mpTarget = new SvgImageNode(maDocument, mpTarget);
314
1
                    mpTarget->parseAttributes(xAttribs);
315
1
                    break;
316
0
                }
317
318
                /// title and description
319
27
                case SVGToken::Title:
320
46
                case SVGToken::Desc:
321
46
                {
322
                    /// new node for Title and/or Desc
323
46
                    mpTarget = new SvgTitleDescNode(aSVGToken, maDocument, mpTarget);
324
46
                    break;
325
27
                }
326
327
                /// gradients
328
0
                case SVGToken::LinearGradient:
329
0
                case SVGToken::RadialGradient:
330
0
                {
331
0
                    mpTarget = new SvgGradientNode(aSVGToken, maDocument, mpTarget);
332
0
                    mpTarget->parseAttributes(xAttribs);
333
0
                    break;
334
0
                }
335
336
                /// gradient stops
337
0
                case SVGToken::Stop:
338
0
                {
339
0
                    mpTarget = new SvgGradientStopNode(maDocument, mpTarget);
340
0
                    mpTarget->parseAttributes(xAttribs);
341
0
                    break;
342
0
                }
343
344
                /// text
345
0
                case SVGToken::Text:
346
0
                {
347
0
                    mpTarget = new SvgTextNode(maDocument, mpTarget);
348
0
                    mpTarget->parseAttributes(xAttribs);
349
0
                    break;
350
0
                }
351
0
                case SVGToken::Tspan:
352
0
                {
353
0
                    mpTarget = new SvgTspanNode(aSVGToken, maDocument, mpTarget);
354
0
                    mpTarget->parseAttributes(xAttribs);
355
0
                    break;
356
0
                }
357
0
                case SVGToken::Tref:
358
0
                {
359
0
                    mpTarget = new SvgTrefNode(maDocument, mpTarget);
360
0
                    mpTarget->parseAttributes(xAttribs);
361
0
                    break;
362
0
                }
363
0
                case SVGToken::TextPath:
364
0
                {
365
0
                    mpTarget = new SvgTextPathNode(maDocument, mpTarget);
366
0
                    mpTarget->parseAttributes(xAttribs);
367
0
                    break;
368
0
                }
369
370
                /// styles (as stylesheets)
371
1.08k
                case SVGToken::Style:
372
1.08k
                {
373
1.08k
                    SvgStyleNode* pNew = new SvgStyleNode(maDocument, mpTarget);
374
1.08k
                    mpTarget = pNew;
375
376
                    // #i125326# there are attributes, read them. This will set isTextCss to false if
377
                    // type attribute is different to "text/css"
378
1.08k
                    mpTarget->parseAttributes(xAttribs);
379
380
1.08k
                    if(pNew->isTextCss())
381
1.08k
                    {
382
                        // if it is a Css style, allow reading text between the start and end tag (see
383
                        // SvgDocHdl::characters for details)
384
1.08k
                        maCssContents.emplace_back();
385
1.08k
                    }
386
1.08k
                    break;
387
0
                }
388
389
                /// structural elements clip-path and mask. Content gets scanned, but
390
                /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
391
0
                case SVGToken::ClipPathNode:
392
0
                {
393
                    /// new node for ClipPath
394
0
                    mpTarget = new SvgClipPathNode(maDocument, mpTarget);
395
0
                    mpTarget->parseAttributes(xAttribs);
396
0
                    break;
397
0
                }
398
0
                case SVGToken::Mask:
399
0
                {
400
                    /// new node for Mask
401
0
                    mpTarget = new SvgMaskNode(maDocument, mpTarget);
402
0
                    mpTarget->parseAttributes(xAttribs);
403
0
                    break;
404
0
                }
405
0
                case SVGToken::FeBlend:
406
0
                {
407
                    /// new node for feBlend
408
0
                    mpTarget = new SvgFeBlendNode(maDocument, mpTarget);
409
0
                    mpTarget->parseAttributes(xAttribs);
410
0
                    break;
411
0
                }
412
0
                case SVGToken::FeColorMatrix:
413
0
                {
414
                    /// new node for feColorMatrix
415
0
                    mpTarget = new SvgFeColorMatrixNode(maDocument, mpTarget);
416
0
                    mpTarget->parseAttributes(xAttribs);
417
0
                    break;
418
0
                }
419
0
                case SVGToken::FeComposite:
420
0
                {
421
                    /// new node for feComposite
422
0
                    mpTarget = new SvgFeCompositeNode(maDocument, mpTarget);
423
0
                    mpTarget->parseAttributes(xAttribs);
424
0
                    break;
425
0
                }
426
0
                case SVGToken::FeDropShadow:
427
0
                {
428
                    /// new node for feDropShadow
429
0
                    mpTarget = new SvgFeDropShadowNode(maDocument, mpTarget);
430
0
                    mpTarget->parseAttributes(xAttribs);
431
0
                    break;
432
0
                }
433
0
                case SVGToken::FeFlood:
434
0
                {
435
                    /// new node for feFlood
436
0
                    mpTarget = new SvgFeFloodNode(maDocument, mpTarget);
437
0
                    mpTarget->parseAttributes(xAttribs);
438
0
                    break;
439
0
                }
440
0
                case SVGToken::FeImage:
441
0
                {
442
                    /// new node for feImage
443
0
                    mpTarget = new SvgFeImageNode(maDocument, mpTarget);
444
0
                    mpTarget->parseAttributes(xAttribs);
445
0
                    break;
446
0
                }
447
0
                case SVGToken::FeGaussianBlur:
448
0
                {
449
                    /// new node for feGaussianBlur
450
0
                    mpTarget = new SvgFeGaussianBlurNode(maDocument, mpTarget);
451
0
                    mpTarget->parseAttributes(xAttribs);
452
0
                    break;
453
0
                }
454
0
                case SVGToken::FeMerge:
455
0
                {
456
                    /// new node for feMerge
457
0
                    mpTarget = new SvgFeMergeNode(aSVGToken, maDocument, mpTarget);
458
0
                    mpTarget->parseAttributes(xAttribs);
459
0
                    break;
460
0
                }
461
0
                case SVGToken::FeMergeNode:
462
0
                {
463
                    /// new node for feMergeNode
464
0
                    mpTarget = new SvgFeMergeNodeNode(maDocument, mpTarget);
465
0
                    mpTarget->parseAttributes(xAttribs);
466
0
                    break;
467
0
                }
468
0
                case SVGToken::FeOffset:
469
0
                {
470
                    /// new node for feOffset
471
0
                    mpTarget = new SvgFeOffsetNode(maDocument, mpTarget);
472
0
                    mpTarget->parseAttributes(xAttribs);
473
0
                    break;
474
0
                }
475
0
                case SVGToken::Filter:
476
0
                {
477
                    /// new node for Filter
478
0
                    mpTarget = new SvgFilterNode(aSVGToken, maDocument, mpTarget);
479
0
                    mpTarget->parseAttributes(xAttribs);
480
0
                    break;
481
0
                }
482
483
                /// structural element marker
484
0
                case SVGToken::Marker:
485
0
                {
486
                    /// new node for marker
487
0
                    mpTarget = new SvgMarkerNode(maDocument, mpTarget);
488
0
                    mpTarget->parseAttributes(xAttribs);
489
0
                    break;
490
0
                }
491
492
                /// structural element pattern
493
0
                case SVGToken::Pattern:
494
0
                {
495
                    /// new node for pattern
496
0
                    mpTarget = new SvgPatternNode(maDocument, mpTarget);
497
0
                    mpTarget->parseAttributes(xAttribs);
498
0
                    break;
499
0
                }
500
501
9.64k
                default:
502
9.64k
                {
503
9.64k
                    mpTarget = new SvgNode(SVGToken::Unknown, maDocument, mpTarget);
504
9.64k
                    break;
505
0
                }
506
29.1k
            }
507
29.1k
        }
508
509
        void SvgDocHdl::endElement( const OUString& aName )
510
1.62k
        {
511
1.62k
            if(aName.isEmpty())
512
0
                return;
513
514
1.62k
            if(!mpTarget)
515
0
                return;
516
517
1.62k
            const SVGToken aSVGToken(StrToSVGToken(aName, false));
518
1.62k
            SvgNode* pTextNode(SVGToken::Text == aSVGToken ? mpTarget : nullptr);
519
1.62k
            SvgStyleNode* pCssStyle(SVGToken::Style == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : nullptr);
520
1.62k
            SvgTitleDescNode* pSvgTitleDescNode(SVGToken::Title == aSVGToken || SVGToken::Desc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : nullptr);
521
522
1.62k
            if(!mpTarget->getParent())
523
45
            {
524
                // last element closing, save this tree
525
45
                maDocument.appendNode(std::unique_ptr<SvgNode>(mpTarget));
526
45
            }
527
528
1.62k
            mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
529
530
1.62k
            if (pSvgTitleDescNode && mpTarget)
531
14
            {
532
14
                const OUString& aText(pSvgTitleDescNode->getText());
533
534
14
                if(!aText.isEmpty())
535
5
                {
536
5
                    mpTarget->parseAttribute(aSVGToken, aText);
537
5
                }
538
14
            }
539
540
1.62k
            if(pCssStyle && pCssStyle->isTextCss())
541
588
            {
542
                // css style parsing
543
588
                if(!maCssContents.empty())
544
588
                {
545
                    // need to interpret css styles and remember them as StyleSheets
546
                    // #125325# Caution! the Css content may contain block comments
547
                    // (see http://www.w3.org/wiki/CSS_basics#CSS_comments). These need
548
                    // to be removed first
549
588
                    const OUString aCommentFreeSource(removeBlockComments(*(maCssContents.end() - 1)));
550
551
588
                    if(aCommentFreeSource.getLength())
552
551
                    {
553
551
                        pCssStyle->addCssStyleSheet(aCommentFreeSource);
554
551
                    }
555
556
588
                    maCssContents.pop_back();
557
588
                }
558
0
                else
559
0
                {
560
0
                    OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
561
0
                }
562
588
            }
563
564
1.62k
            if(pTextNode)
565
0
            {
566
                // cleanup read strings
567
                // First pass: handle whitespace. This works in a way that handling a following
568
                // node may append a space to a previous node; so correct line width calculation
569
                // may only happen after this pass finishes
570
0
                walkRecursive(pTextNode, static_cast<SvgTspanNode*>(pTextNode), nullptr, whiteSpaceHandling);
571
                // Second pass: calculate line widths
572
0
                walkRecursive(pTextNode, static_cast<SvgTspanNode*>(pTextNode), nullptr, calcTextLineWidths);
573
0
            }
574
1.62k
        }
575
576
        void SvgDocHdl::characters( const OUString& aChars )
577
22.8k
        {
578
22.8k
            const sal_uInt32 nLength(aChars.getLength());
579
580
22.8k
            if(!(mpTarget && nLength))
581
0
                return;
582
583
22.8k
            switch(mpTarget->getType())
584
22.8k
            {
585
0
                case SVGToken::Text:
586
0
                case SVGToken::Tspan:
587
0
                case SVGToken::TextPath:
588
0
                {
589
0
                    const auto& rChilds = mpTarget->getChildren();
590
591
0
                    if(!rChilds.empty())
592
0
                    {
593
0
                        SvgNode* pChild = rChilds[rChilds.size() - 1].get();
594
0
                        if ( pChild->getType() == SVGToken::Character )
595
0
                        {
596
0
                            SvgCharacterNode& rSvgCharacterNode = static_cast< SvgCharacterNode& >(*pChild);
597
598
                            // concatenate to current character span
599
0
                            rSvgCharacterNode.concatenate(aChars);
600
0
                            break;
601
0
                        }
602
0
                    }
603
604
                    // add character span as simplified tspan (no arguments)
605
                    // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
606
0
                    new SvgCharacterNode(maDocument, mpTarget, aChars);
607
0
                    break;
608
0
                }
609
1.02k
                case SVGToken::Style:
610
1.02k
                {
611
1.02k
                    SvgStyleNode& rSvgStyleNode = static_cast< SvgStyleNode& >(*mpTarget);
612
613
1.02k
                    if(rSvgStyleNode.isTextCss())
614
1.02k
                    {
615
                        // collect characters for css style
616
1.02k
                        if(!maCssContents.empty())
617
1.02k
                        {
618
1.02k
                            const OUString aTrimmedChars(aChars.trim());
619
620
1.02k
                            if(!aTrimmedChars.isEmpty())
621
977
                            {
622
977
                                std::vector< OUString >::iterator aString(maCssContents.end() - 1);
623
977
                                (*aString) += aTrimmedChars;
624
977
                            }
625
1.02k
                        }
626
0
                        else
627
0
                        {
628
0
                            OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
629
0
                        }
630
1.02k
                    }
631
1.02k
                    break;
632
0
                }
633
10
                case SVGToken::Title:
634
29
                case SVGToken::Desc:
635
29
                {
636
29
                    SvgTitleDescNode& rSvgTitleDescNode = static_cast< SvgTitleDescNode& >(*mpTarget);
637
638
                    // add text directly to SvgTitleDescNode
639
29
                    rSvgTitleDescNode.concatenate(aChars);
640
29
                    break;
641
10
                }
642
21.7k
                default:
643
21.7k
                {
644
                    // characters not used by a known node
645
21.7k
                    break;
646
10
                }
647
22.8k
            }
648
22.8k
        }
649
650
        void SvgDocHdl::ignorableWhitespace(const OUString& /*aWhitespaces*/)
651
0
        {
652
0
        }
653
654
        void SvgDocHdl::processingInstruction(const OUString& /*aTarget*/, const OUString& /*aData*/)
655
2.04k
        {
656
2.04k
        }
657
658
        void SvgDocHdl::setDocumentLocator(const uno::Reference< xml::sax::XLocator >& /*xLocator*/)
659
18.8k
        {
660
18.8k
        }
661
} // end of namespace svgio
662
663
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */