/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: */ |