tdf#159660: Add support for feMerge and feMergeNode
[libreoffice.git] / svgio / source / svgreader / svgdocumenthandler.cxx
blobe45dcad857a9f180443bc1b850cc3888c1bfa0d2
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 <svgfecolormatrixnode.hxx>
45 #include <svgfedropshadownode.hxx>
46 #include <svgfefloodnode.hxx>
47 #include <svgfeimagenode.hxx>
48 #include <svgfegaussianblurnode.hxx>
49 #include <svgfemergenode.hxx>
50 #include <svgfemergenodenode.hxx>
51 #include <svgfeoffsetnode.hxx>
52 #include <svgfilternode.hxx>
53 #include <svgmasknode.hxx>
54 #include <svgmarkernode.hxx>
55 #include <svgpatternnode.hxx>
56 #include <svgtitledescnode.hxx>
57 #include <sal/log.hxx>
58 #include <osl/diagnose.h>
60 #include <com/sun/star/lang/Locale.hpp>
61 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
63 using namespace com::sun::star;
65 namespace svgio::svgreader
68 namespace
70 svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgNode const * pNode, svgio::svgreader::SvgTspanNode* pParentLine, svgio::svgreader::SvgCharacterNode* pLast)
72 if(pNode)
74 const auto& rChilds = pNode->getChildren();
75 const sal_uInt32 nCount(rChilds.size());
77 for(sal_uInt32 a(0); a < nCount; a++)
79 svgio::svgreader::SvgNode* pCandidate = rChilds[a].get();
81 if(pCandidate)
83 switch(pCandidate->getType())
85 case SVGToken::Character:
87 // clean whitespace in text span
88 svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);
90 pCharNode->setParentLine(pParentLine);
92 pCharNode->whiteSpaceHandling();
93 pLast = pCharNode->addGap(pLast);
95 double fTextWidth(0.0);
97 const SvgStyleAttributes* pSvgStyleAttributes = pCharNode->getSvgStyleAttributes();
99 if(pSvgStyleAttributes)
101 const drawinglayer::attribute::FontAttribute aFontAttribute(
102 svgio::svgreader::SvgCharacterNode::getFontAttribute(*pSvgStyleAttributes));
104 double fFontWidth(pSvgStyleAttributes->getFontSizeNumber().solve(*pCharNode));
105 double fFontHeight(fFontWidth);
107 css::lang::Locale aLocale;
109 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
110 aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale);
111 fTextWidth = aTextLayouterDevice.getTextWidth(pCharNode->getText(), 0.0, pCharNode->getText().getLength());
114 pParentLine->concatenateTextLineWidth(fTextWidth);
115 break;
117 case SVGToken::Tspan:
119 svgio::svgreader::SvgTspanNode* pTspanNode = static_cast< svgio::svgreader::SvgTspanNode* >(pCandidate);
121 // If x or y exist it means it's a new line of text
122 if(!pTspanNode->getX().empty() || !pTspanNode->getY().empty())
123 pParentLine = pTspanNode;
125 // recursively clean whitespaces in subhierarchy
126 pLast = whiteSpaceHandling(pCandidate, pParentLine, pLast);
127 break;
129 case SVGToken::TextPath:
130 case SVGToken::Tref:
132 // recursively clean whitespaces in subhierarchy
133 pLast = whiteSpaceHandling(pCandidate, pParentLine, pLast);
134 break;
136 default:
138 OSL_ENSURE(false, "Unexpected token inside SVGTokenText (!)");
139 break;
146 return pLast;
149 } // end anonymous namespace
151 SvgDocHdl::SvgDocHdl(const OUString& aAbsolutePath)
152 : maDocument(aAbsolutePath),
153 mpTarget(nullptr)
157 SvgDocHdl::~SvgDocHdl()
159 if (mpTarget)
161 OSL_ENSURE(false, "SvgDocHdl destructed with active target (!)");
163 while (mpTarget->getParent())
164 mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
166 const SvgNodeVector& rOwnedTopLevels = maDocument.getSvgNodeVector();
167 if (std::none_of(rOwnedTopLevels.begin(), rOwnedTopLevels.end(),
168 [&](std::unique_ptr<SvgNode> const & p) { return p.get() == mpTarget; }))
169 delete mpTarget;
171 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl destructed with active css style stack entry (!)");
174 void SvgDocHdl::startDocument( )
176 OSL_ENSURE(!mpTarget, "Already a target at document start (!)");
177 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl startDocument with active css style stack entry (!)");
180 void SvgDocHdl::endDocument( )
182 OSL_ENSURE(!mpTarget, "Still a target at document end (!)");
183 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl endDocument with active css style stack entry (!)");
186 void SvgDocHdl::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs )
188 if(aName.isEmpty())
189 return;
191 const SVGToken aSVGToken(StrToSVGToken(aName, false));
193 switch (aSVGToken)
195 /// structural elements
196 case SVGToken::Symbol:
198 /// new basic node for Symbol. Content gets scanned, but
199 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
200 mpTarget = new SvgSymbolNode(maDocument, mpTarget);
201 mpTarget->parseAttributes(xAttribs);
202 break;
204 case SVGToken::Switch: //TODO: Support switch element
205 case SVGToken::Defs:
206 case SVGToken::G:
208 /// new node for Defs/G
209 mpTarget = new SvgGNode(aSVGToken, maDocument, mpTarget);
210 mpTarget->parseAttributes(xAttribs);
211 break;
213 case SVGToken::Svg:
215 /// new node for Svg
216 mpTarget = new SvgSvgNode(maDocument, mpTarget);
217 mpTarget->parseAttributes(xAttribs);
218 break;
220 case SVGToken::Use:
222 /// new node for Use
223 mpTarget = new SvgUseNode(maDocument, mpTarget);
224 mpTarget->parseAttributes(xAttribs);
225 break;
227 case SVGToken::A:
229 /// new node for A
230 mpTarget = new SvgANode(maDocument, mpTarget);
231 mpTarget->parseAttributes(xAttribs);
232 break;
235 /// shape elements
236 case SVGToken::Circle:
238 /// new node for Circle
239 mpTarget = new SvgCircleNode(maDocument, mpTarget);
240 mpTarget->parseAttributes(xAttribs);
241 break;
243 case SVGToken::Ellipse:
245 /// new node for Ellipse
246 mpTarget = new SvgEllipseNode(maDocument, mpTarget);
247 mpTarget->parseAttributes(xAttribs);
248 break;
250 case SVGToken::Line:
252 /// new node for Line
253 mpTarget = new SvgLineNode(maDocument, mpTarget);
254 mpTarget->parseAttributes(xAttribs);
255 break;
257 case SVGToken::Path:
259 /// new node for Path
260 mpTarget = new SvgPathNode(maDocument, mpTarget);
261 mpTarget->parseAttributes(xAttribs);
262 break;
264 case SVGToken::Polygon:
266 /// new node for Polygon
267 mpTarget = new SvgPolyNode(aSVGToken, maDocument, mpTarget);
268 mpTarget->parseAttributes(xAttribs);
269 break;
271 case SVGToken::Polyline:
273 /// new node for Polyline
274 mpTarget = new SvgPolyNode(aSVGToken, maDocument, mpTarget);
275 mpTarget->parseAttributes(xAttribs);
276 break;
278 case SVGToken::Rect:
280 /// new node for Rect
281 mpTarget = new SvgRectNode(maDocument, mpTarget);
282 mpTarget->parseAttributes(xAttribs);
283 break;
285 case SVGToken::Image:
287 /// new node for Image
288 mpTarget = new SvgImageNode(maDocument, mpTarget);
289 mpTarget->parseAttributes(xAttribs);
290 break;
293 /// title and description
294 case SVGToken::Title:
295 case SVGToken::Desc:
297 /// new node for Title and/or Desc
298 mpTarget = new SvgTitleDescNode(aSVGToken, maDocument, mpTarget);
299 break;
302 /// gradients
303 case SVGToken::LinearGradient:
304 case SVGToken::RadialGradient:
306 mpTarget = new SvgGradientNode(aSVGToken, maDocument, mpTarget);
307 mpTarget->parseAttributes(xAttribs);
308 break;
311 /// gradient stops
312 case SVGToken::Stop:
314 mpTarget = new SvgGradientStopNode(maDocument, mpTarget);
315 mpTarget->parseAttributes(xAttribs);
316 break;
319 /// text
320 case SVGToken::Text:
322 mpTarget = new SvgTextNode(maDocument, mpTarget);
323 mpTarget->parseAttributes(xAttribs);
324 break;
326 case SVGToken::Tspan:
328 mpTarget = new SvgTspanNode(aSVGToken, maDocument, mpTarget);
329 mpTarget->parseAttributes(xAttribs);
330 break;
332 case SVGToken::Tref:
334 mpTarget = new SvgTrefNode(maDocument, mpTarget);
335 mpTarget->parseAttributes(xAttribs);
336 break;
338 case SVGToken::TextPath:
340 mpTarget = new SvgTextPathNode(maDocument, mpTarget);
341 mpTarget->parseAttributes(xAttribs);
342 break;
345 /// styles (as stylesheets)
346 case SVGToken::Style:
348 SvgStyleNode* pNew = new SvgStyleNode(maDocument, mpTarget);
349 mpTarget = pNew;
351 // #i125326# there are attributes, read them. This will set isTextCss to false if
352 // type attribute is different to "text/css"
353 mpTarget->parseAttributes(xAttribs);
355 if(pNew->isTextCss())
357 // if it is a Css style, allow reading text between the start and end tag (see
358 // SvgDocHdl::characters for details)
359 maCssContents.emplace_back();
361 break;
364 /// structural elements clip-path and mask. Content gets scanned, but
365 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
366 case SVGToken::ClipPathNode:
368 /// new node for ClipPath
369 mpTarget = new SvgClipPathNode(maDocument, mpTarget);
370 mpTarget->parseAttributes(xAttribs);
371 break;
373 case SVGToken::Mask:
375 /// new node for Mask
376 mpTarget = new SvgMaskNode(maDocument, mpTarget);
377 mpTarget->parseAttributes(xAttribs);
378 break;
380 case SVGToken::FeColorMatrix:
382 /// new node for feColorMatrix
383 mpTarget = new SvgFeColorMatrixNode(maDocument, mpTarget);
384 mpTarget->parseAttributes(xAttribs);
385 break;
387 case SVGToken::FeDropShadow:
389 /// new node for feDropShadow
390 mpTarget = new SvgFeDropShadowNode(maDocument, mpTarget);
391 mpTarget->parseAttributes(xAttribs);
392 break;
394 case SVGToken::FeFlood:
396 /// new node for feFlood
397 mpTarget = new SvgFeFloodNode(maDocument, mpTarget);
398 mpTarget->parseAttributes(xAttribs);
399 break;
401 case SVGToken::FeImage:
403 /// new node for feImage
404 mpTarget = new SvgFeImageNode(maDocument, mpTarget);
405 mpTarget->parseAttributes(xAttribs);
406 break;
408 case SVGToken::FeGaussianBlur:
410 /// new node for feGaussianBlur
411 mpTarget = new SvgFeGaussianBlurNode(maDocument, mpTarget);
412 mpTarget->parseAttributes(xAttribs);
413 break;
415 case SVGToken::FeMerge:
417 /// new node for feMerge
418 mpTarget = new SvgFeMergeNode(aSVGToken, maDocument, mpTarget);
419 mpTarget->parseAttributes(xAttribs);
420 break;
422 case SVGToken::FeMergeNode:
424 /// new node for feMergeNode
425 mpTarget = new SvgFeMergeNodeNode(maDocument, mpTarget);
426 mpTarget->parseAttributes(xAttribs);
427 break;
429 case SVGToken::FeOffset:
431 /// new node for feOffset
432 mpTarget = new SvgFeOffsetNode(maDocument, mpTarget);
433 mpTarget->parseAttributes(xAttribs);
434 break;
436 case SVGToken::Filter:
438 /// new node for Filter
439 mpTarget = new SvgFilterNode(aSVGToken, maDocument, mpTarget);
440 mpTarget->parseAttributes(xAttribs);
441 break;
444 /// structural element marker
445 case SVGToken::Marker:
447 /// new node for marker
448 mpTarget = new SvgMarkerNode(maDocument, mpTarget);
449 mpTarget->parseAttributes(xAttribs);
450 break;
453 /// structural element pattern
454 case SVGToken::Pattern:
456 /// new node for pattern
457 mpTarget = new SvgPatternNode(maDocument, mpTarget);
458 mpTarget->parseAttributes(xAttribs);
459 break;
462 default:
464 mpTarget = new SvgNode(SVGToken::Unknown, maDocument, mpTarget);
465 break;
470 void SvgDocHdl::endElement( const OUString& aName )
472 if(aName.isEmpty())
473 return;
475 if(!mpTarget)
476 return;
478 const SVGToken aSVGToken(StrToSVGToken(aName, false));
479 SvgNode* pTextNode(SVGToken::Text == aSVGToken ? mpTarget : nullptr);
480 SvgStyleNode* pCssStyle(SVGToken::Style == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : nullptr);
481 SvgTitleDescNode* pSvgTitleDescNode(SVGToken::Title == aSVGToken || SVGToken::Desc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : nullptr);
483 if(!mpTarget->getParent())
485 // last element closing, save this tree
486 maDocument.appendNode(std::unique_ptr<SvgNode>(mpTarget));
489 mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
491 if (pSvgTitleDescNode && mpTarget)
493 const OUString& aText(pSvgTitleDescNode->getText());
495 if(!aText.isEmpty())
497 mpTarget->parseAttribute(aSVGToken, aText);
501 if(pCssStyle && pCssStyle->isTextCss())
503 // css style parsing
504 if(!maCssContents.empty())
506 // need to interpret css styles and remember them as StyleSheets
507 // #125325# Caution! the Css content may contain block comments
508 // (see http://www.w3.org/wiki/CSS_basics#CSS_comments). These need
509 // to be removed first
510 const OUString aCommentFreeSource(removeBlockComments(*(maCssContents.end() - 1)));
512 if(aCommentFreeSource.getLength())
514 pCssStyle->addCssStyleSheet(aCommentFreeSource);
517 maCssContents.pop_back();
519 else
521 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
525 if(pTextNode)
527 // cleanup read strings
528 whiteSpaceHandling(pTextNode, static_cast< SvgTspanNode*>(pTextNode), nullptr);
532 void SvgDocHdl::characters( const OUString& aChars )
534 const sal_uInt32 nLength(aChars.getLength());
536 if(!(mpTarget && nLength))
537 return;
539 switch(mpTarget->getType())
541 case SVGToken::Text:
542 case SVGToken::Tspan:
543 case SVGToken::TextPath:
545 const auto& rChilds = mpTarget->getChildren();
547 if(!rChilds.empty())
549 SvgNode* pChild = rChilds[rChilds.size() - 1].get();
550 if ( pChild->getType() == SVGToken::Character )
552 SvgCharacterNode& rSvgCharacterNode = static_cast< SvgCharacterNode& >(*pChild);
554 // concatenate to current character span
555 rSvgCharacterNode.concatenate(aChars);
556 break;
560 // add character span as simplified tspan (no arguments)
561 // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
562 new SvgCharacterNode(maDocument, mpTarget, aChars);
563 break;
565 case SVGToken::Style:
567 SvgStyleNode& rSvgStyleNode = static_cast< SvgStyleNode& >(*mpTarget);
569 if(rSvgStyleNode.isTextCss())
571 // collect characters for css style
572 if(!maCssContents.empty())
574 const OUString aTrimmedChars(aChars.trim());
576 if(!aTrimmedChars.isEmpty())
578 std::vector< OUString >::iterator aString(maCssContents.end() - 1);
579 (*aString) += aTrimmedChars;
582 else
584 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
587 break;
589 case SVGToken::Title:
590 case SVGToken::Desc:
592 SvgTitleDescNode& rSvgTitleDescNode = static_cast< SvgTitleDescNode& >(*mpTarget);
594 // add text directly to SvgTitleDescNode
595 rSvgTitleDescNode.concatenate(aChars);
596 break;
598 default:
600 // characters not used by a known node
601 break;
606 void SvgDocHdl::ignorableWhitespace(const OUString& /*aWhitespaces*/)
610 void SvgDocHdl::processingInstruction(const OUString& /*aTarget*/, const OUString& /*aData*/)
614 void SvgDocHdl::setDocumentLocator(const uno::Reference< xml::sax::XLocator >& /*xLocator*/)
617 } // end of namespace svgio
619 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */