Teach symstore more duplicated DLLs
[LibreOffice.git] / svgio / source / svgreader / svgdocumenthandler.cxx
blob7ab843dfa90669051122c46578d18f7298702062
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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 <svgsymbolnode.hxx>
31 #include <svgusenode.hxx>
32 #include <svgcirclenode.hxx>
33 #include <svgellipsenode.hxx>
34 #include <svglinenode.hxx>
35 #include <svgpolynode.hxx>
36 #include <svgtextnode.hxx>
37 #include <svgcharacternode.hxx>
38 #include <svgtspannode.hxx>
39 #include <svgtrefnode.hxx>
40 #include <svgtextpathnode.hxx>
41 #include <svgstylenode.hxx>
42 #include <svgimagenode.hxx>
43 #include <svgclippathnode.hxx>
44 #include <svgmasknode.hxx>
45 #include <svgmarkernode.hxx>
46 #include <svgpatternnode.hxx>
47 #include <svgtitledescnode.hxx>
48 #include <sal/log.hxx>
50 using namespace com::sun::star;
52 namespace
54 svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgNode const * pNode, svgio::svgreader::SvgCharacterNode* pLast)
56 if(pNode)
58 const auto& rChilds = pNode->getChildren();
59 const sal_uInt32 nCount(rChilds.size());
61 for(sal_uInt32 a(0); a < nCount; a++)
63 svgio::svgreader::SvgNode* pCandidate = rChilds[a].get();
65 if(pCandidate)
67 switch(pCandidate->getType())
69 case svgio::svgreader::SVGTokenCharacter:
71 // clean whitespace in text span
72 svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);
73 pCharNode->whiteSpaceHandling();
75 // pCharNode may have lost all text. If that's the case, ignore
76 // as invalid character node
77 if(!pCharNode->getText().isEmpty())
79 if(pLast)
81 bool bAddGap(true);
82 static bool bNoGapsForBaselineShift(true); // loplugin:constvars:ignore
84 if(bNoGapsForBaselineShift)
86 // With this option a baseline shift between two char parts ('words')
87 // will not add a space 'gap' to the end of the (non-last) word. This
88 // seems to be the standard behaviour, see last bugdoc attached #122524#
89 const svgio::svgreader::SvgStyleAttributes* pStyleLast = pLast->getSvgStyleAttributes();
90 const svgio::svgreader::SvgStyleAttributes* pStyleCurrent = pCandidate->getSvgStyleAttributes();
92 if(pStyleLast && pStyleCurrent && pStyleLast->getBaselineShift() != pStyleCurrent->getBaselineShift())
94 bAddGap = false;
98 // add in-between whitespace (single space) to last
99 // known character node
100 if(bAddGap)
102 pLast->addGap();
106 // remember new last corrected character node
107 pLast = pCharNode;
109 break;
111 case svgio::svgreader::SVGTokenTspan:
112 case svgio::svgreader::SVGTokenTextPath:
113 case svgio::svgreader::SVGTokenTref:
115 // recursively clean whitespaces in subhierarchy
116 pLast = whiteSpaceHandling(pCandidate, pLast);
117 break;
119 default:
121 OSL_ENSURE(false, "Unexpected token inside SVGTokenText (!)");
122 break;
129 return pLast;
134 namespace svgio
136 namespace svgreader
138 SvgDocHdl::SvgDocHdl(const OUString& aAbsolutePath)
139 : maDocument(aAbsolutePath),
140 mpTarget(nullptr),
141 maCssContents(),
142 bSkip(false)
146 SvgDocHdl::~SvgDocHdl()
148 if (mpTarget)
150 OSL_ENSURE(false, "SvgDocHdl destructed with active target (!)");
152 while (mpTarget->getParent())
153 mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
155 const SvgNodeVector& rOwnedTopLevels = maDocument.getSvgNodeVector();
156 if (std::none_of(rOwnedTopLevels.begin(), rOwnedTopLevels.end(),
157 [&](std::unique_ptr<SvgNode> const & p) { return p.get() == mpTarget; }))
158 delete mpTarget;
160 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl destructed with active css style stack entry (!)");
163 void SvgDocHdl::startDocument( )
165 OSL_ENSURE(!mpTarget, "Already a target at document start (!)");
166 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl startDocument with active css style stack entry (!)");
169 void SvgDocHdl::endDocument( )
171 OSL_ENSURE(!mpTarget, "Still a target at document end (!)");
172 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl endDocument with active css style stack entry (!)");
175 void SvgDocHdl::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs )
177 if (bSkip)
178 return;
179 if(aName.isEmpty())
180 return;
182 const SVGToken aSVGToken(StrToSVGToken(aName, false));
184 switch(aSVGToken)
186 /// structural elements
187 case SVGTokenSymbol:
189 /// new basic node for Symbol. Content gets scanned, but
190 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
191 mpTarget = new SvgSymbolNode(maDocument, mpTarget);
192 mpTarget->parseAttributes(xAttribs);
193 break;
195 case SVGTokenDefs:
196 case SVGTokenG:
198 /// new node for Defs/G
199 mpTarget = new SvgGNode(aSVGToken, maDocument, mpTarget);
200 mpTarget->parseAttributes(xAttribs);
201 break;
203 case SVGTokenSvg:
205 /// new node for Svg
206 mpTarget = new SvgSvgNode(maDocument, mpTarget);
207 mpTarget->parseAttributes(xAttribs);
208 break;
210 case SVGTokenUse:
212 /// new node for Use
213 mpTarget = new SvgUseNode(maDocument, mpTarget);
214 mpTarget->parseAttributes(xAttribs);
215 break;
217 case SVGTokenA:
219 /// new node for A
220 mpTarget = new SvgANode(maDocument, mpTarget);
221 mpTarget->parseAttributes(xAttribs);
222 break;
225 /// shape elements
226 case SVGTokenCircle:
228 /// new node for Circle
229 mpTarget = new SvgCircleNode(maDocument, mpTarget);
230 mpTarget->parseAttributes(xAttribs);
231 break;
233 case SVGTokenEllipse:
235 /// new node for Ellipse
236 mpTarget = new SvgEllipseNode(maDocument, mpTarget);
237 mpTarget->parseAttributes(xAttribs);
238 break;
240 case SVGTokenLine:
242 /// new node for Line
243 mpTarget = new SvgLineNode(maDocument, mpTarget);
244 mpTarget->parseAttributes(xAttribs);
245 break;
247 case SVGTokenPath:
249 /// new node for Path
250 mpTarget = new SvgPathNode(maDocument, mpTarget);
251 mpTarget->parseAttributes(xAttribs);
252 break;
254 case SVGTokenPolygon:
256 /// new node for Polygon
257 mpTarget = new SvgPolyNode(maDocument, mpTarget, false);
258 mpTarget->parseAttributes(xAttribs);
259 break;
261 case SVGTokenPolyline:
263 /// new node for Polyline
264 mpTarget = new SvgPolyNode(maDocument, mpTarget, true);
265 mpTarget->parseAttributes(xAttribs);
266 break;
268 case SVGTokenRect:
270 /// new node for Rect
271 mpTarget = new SvgRectNode(maDocument, mpTarget);
272 mpTarget->parseAttributes(xAttribs);
273 break;
275 case SVGTokenImage:
277 /// new node for Image
278 mpTarget = new SvgImageNode(maDocument, mpTarget);
279 mpTarget->parseAttributes(xAttribs);
280 break;
283 /// title and description
284 case SVGTokenTitle:
285 case SVGTokenDesc:
287 /// new node for Title and/or Desc
288 mpTarget = new SvgTitleDescNode(aSVGToken, maDocument, mpTarget);
289 break;
292 /// gradients
293 case SVGTokenLinearGradient:
294 case SVGTokenRadialGradient:
296 mpTarget = new SvgGradientNode(aSVGToken, maDocument, mpTarget);
297 mpTarget->parseAttributes(xAttribs);
298 break;
301 /// gradient stops
302 case SVGTokenStop:
304 mpTarget = new SvgGradientStopNode(maDocument, mpTarget);
305 mpTarget->parseAttributes(xAttribs);
306 break;
309 /// text
310 case SVGTokenText:
312 mpTarget = new SvgTextNode(maDocument, mpTarget);
313 mpTarget->parseAttributes(xAttribs);
314 break;
316 case SVGTokenTspan:
318 mpTarget = new SvgTspanNode(maDocument, mpTarget);
319 mpTarget->parseAttributes(xAttribs);
320 break;
322 case SVGTokenTref:
324 mpTarget = new SvgTrefNode(maDocument, mpTarget);
325 mpTarget->parseAttributes(xAttribs);
326 break;
328 case SVGTokenTextPath:
330 mpTarget = new SvgTextPathNode(maDocument, mpTarget);
331 mpTarget->parseAttributes(xAttribs);
332 break;
335 /// styles (as stylesheets)
336 case SVGTokenStyle:
338 SvgStyleNode* pNew = new SvgStyleNode(maDocument, mpTarget);
339 mpTarget = pNew;
340 const sal_uInt32 nAttributes(xAttribs->getLength());
342 if(0 == nAttributes)
344 // #i125326# no attributes, thus also no type="text/css". This is allowed to be missing,
345 // thus do mark this style as CssStyle. This is required to read the contained
346 // text (which defines the css style)
347 pNew->setTextCss(true);
349 else
351 // #i125326# there are attributes, read them. This will set isTextCss to true if
352 // a type="text/css" is contained as exact match, else not
353 mpTarget->parseAttributes(xAttribs);
356 if(pNew->isTextCss())
358 // if it is a Css style, allow reading text between the start and end tag (see
359 // SvgDocHdl::characters for details)
360 maCssContents.emplace_back();
362 break;
365 /// structural elements clip-path and mask. Content gets scanned, but
366 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
367 case SVGTokenClipPathNode:
369 /// new node for ClipPath
370 mpTarget = new SvgClipPathNode(maDocument, mpTarget);
371 mpTarget->parseAttributes(xAttribs);
372 break;
374 case SVGTokenMask:
376 /// new node for Mask
377 mpTarget = new SvgMaskNode(maDocument, mpTarget);
378 mpTarget->parseAttributes(xAttribs);
379 break;
382 /// structural element marker
383 case SVGTokenMarker:
385 /// new node for marker
386 mpTarget = new SvgMarkerNode(maDocument, mpTarget);
387 mpTarget->parseAttributes(xAttribs);
388 break;
391 /// structural element pattern
392 case SVGTokenPattern:
394 /// new node for pattern
395 mpTarget = new SvgPatternNode(maDocument, mpTarget);
396 mpTarget->parseAttributes(xAttribs);
397 break;
400 // ignore FlowRoot and child nodes
401 case SVGTokenFlowRoot:
403 bSkip = true;
404 break;
407 default:
409 /// invalid token, ignore
410 SAL_WARN( "svgio", "Unknown Base SvgToken <" + aName + "> (!)" );
411 break;
416 void SvgDocHdl::endElement( const OUString& aName )
418 if(aName.isEmpty())
419 return;
421 const SVGToken aSVGToken(StrToSVGToken(aName, false));
422 SvgNode* pWhitespaceCheck(SVGTokenText == aSVGToken ? mpTarget : nullptr);
423 SvgStyleNode* pCssStyle(SVGTokenStyle == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : nullptr);
424 SvgTitleDescNode* pSvgTitleDescNode(SVGTokenTitle == aSVGToken || SVGTokenDesc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : nullptr);
426 // if we are in skipping mode and we reach the flowRoot end tag: stop skipping mode
427 if(bSkip && aSVGToken == SVGTokenFlowRoot)
428 bSkip = false;
429 // we are in skipping mode: do nothing until we found the flowRoot end tag
430 else if(bSkip)
431 return;
433 switch(aSVGToken)
435 /// valid tokens for which a new one was created
437 /// structural elements
438 case SVGTokenDefs:
439 case SVGTokenG:
440 case SVGTokenSvg:
441 case SVGTokenSymbol:
442 case SVGTokenUse:
443 case SVGTokenA:
445 /// shape elements
446 case SVGTokenCircle:
447 case SVGTokenEllipse:
448 case SVGTokenLine:
449 case SVGTokenPath:
450 case SVGTokenPolygon:
451 case SVGTokenPolyline:
452 case SVGTokenRect:
453 case SVGTokenImage:
455 /// title and description
456 case SVGTokenTitle:
457 case SVGTokenDesc:
459 /// gradients
460 case SVGTokenLinearGradient:
461 case SVGTokenRadialGradient:
463 /// gradient stops
464 case SVGTokenStop:
466 /// text
467 case SVGTokenText:
468 case SVGTokenTspan:
469 case SVGTokenTextPath:
470 case SVGTokenTref:
472 /// styles (as stylesheets)
473 case SVGTokenStyle:
475 /// structural elements clip-path and mask
476 case SVGTokenClipPathNode:
477 case SVGTokenMask:
479 /// structural element marker
480 case SVGTokenMarker:
482 /// structural element pattern
483 case SVGTokenPattern:
485 /// content handling after parsing
487 if(mpTarget)
489 if(!mpTarget->getParent())
491 // last element closing, save this tree
492 maDocument.appendNode(std::unique_ptr<SvgNode>(mpTarget));
495 mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
497 else
499 OSL_ENSURE(false, "Closing token, but no context (!)");
501 break;
503 default:
505 /// invalid token, ignore
509 if(pSvgTitleDescNode && mpTarget)
511 const OUString& aText(pSvgTitleDescNode->getText());
513 if(!aText.isEmpty())
515 if(SVGTokenTitle == aSVGToken)
517 mpTarget->parseAttribute(getStrTitle(), aSVGToken, aText);
519 else // if(SVGTokenDesc == aSVGToken)
521 mpTarget->parseAttribute(getStrDesc(), aSVGToken, aText);
526 if(pCssStyle && pCssStyle->isTextCss())
528 // css style parsing
529 if(!maCssContents.empty())
531 // need to interpret css styles and remember them as StyleSheets
532 // #125325# Caution! the Css content may contain block comments
533 // (see http://www.w3.org/wiki/CSS_basics#CSS_comments). These need
534 // to be removed first
535 const OUString aCommentFreeSource(removeBlockComments(*(maCssContents.end() - 1)));
537 if(aCommentFreeSource.getLength())
539 pCssStyle->addCssStyleSheet(aCommentFreeSource);
542 maCssContents.pop_back();
544 else
546 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
550 if(pWhitespaceCheck)
552 // cleanup read strings
553 whiteSpaceHandling(pWhitespaceCheck, nullptr);
557 void SvgDocHdl::characters( const OUString& aChars )
559 const sal_uInt32 nLength(aChars.getLength());
561 if(!(mpTarget && nLength))
562 return;
564 switch(mpTarget->getType())
566 case SVGTokenText:
567 case SVGTokenTspan:
568 case SVGTokenTextPath:
570 const auto& rChilds = mpTarget->getChildren();
571 SvgCharacterNode* pTarget = nullptr;
573 if(!rChilds.empty())
575 pTarget = dynamic_cast< SvgCharacterNode* >(rChilds[rChilds.size() - 1].get());
578 if(pTarget)
580 // concatenate to current character span
581 pTarget->concatenate(aChars);
583 else
585 // add character span as simplified tspan (no arguments)
586 // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
587 new SvgCharacterNode(maDocument, mpTarget, aChars);
589 break;
591 case SVGTokenStyle:
593 SvgStyleNode& rSvgStyleNode = static_cast< SvgStyleNode& >(*mpTarget);
595 if(rSvgStyleNode.isTextCss())
597 // collect characters for css style
598 if(!maCssContents.empty())
600 const OUString aTrimmedChars(aChars.trim());
602 if(!aTrimmedChars.isEmpty())
604 std::vector< OUString >::iterator aString(maCssContents.end() - 1);
605 (*aString) += aTrimmedChars;
608 else
610 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
613 break;
615 case SVGTokenTitle:
616 case SVGTokenDesc:
618 SvgTitleDescNode& rSvgTitleDescNode = static_cast< SvgTitleDescNode& >(*mpTarget);
620 // add text directly to SvgTitleDescNode
621 rSvgTitleDescNode.concatenate(aChars);
622 break;
624 default:
626 // characters not used by a known node
627 break;
632 void SvgDocHdl::ignorableWhitespace(const OUString& /*aWhitespaces*/)
636 void SvgDocHdl::processingInstruction(const OUString& /*aTarget*/, const OUString& /*aData*/)
640 void SvgDocHdl::setDocumentLocator(const uno::Reference< xml::sax::XLocator >& /*xLocator*/)
643 } // end of namespace svgreader
644 } // end of namespace svgio
646 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */