1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <svgio/svgreader/svgdocumenthandler.hxx>
21 #include <svgio/svgreader/svgtoken.hxx>
22 #include <svgio/svgreader/svgsvgnode.hxx>
23 #include <svgio/svgreader/svggnode.hxx>
24 #include <svgio/svgreader/svgnode.hxx>
25 #include <svgio/svgreader/svgpathnode.hxx>
26 #include <svgio/svgreader/svgrectnode.hxx>
27 #include <svgio/svgreader/svggradientnode.hxx>
28 #include <svgio/svgreader/svggradientstopnode.hxx>
29 #include <svgio/svgreader/svgsymbolnode.hxx>
30 #include <svgio/svgreader/svgusenode.hxx>
31 #include <svgio/svgreader/svgcirclenode.hxx>
32 #include <svgio/svgreader/svgellipsenode.hxx>
33 #include <svgio/svgreader/svglinenode.hxx>
34 #include <svgio/svgreader/svgpolynode.hxx>
35 #include <svgio/svgreader/svgtextnode.hxx>
36 #include <svgio/svgreader/svgcharacternode.hxx>
37 #include <svgio/svgreader/svgtspannode.hxx>
38 #include <svgio/svgreader/svgtrefnode.hxx>
39 #include <svgio/svgreader/svgtextpathnode.hxx>
40 #include <svgio/svgreader/svgstylenode.hxx>
41 #include <svgio/svgreader/svgimagenode.hxx>
42 #include <svgio/svgreader/svgclippathnode.hxx>
43 #include <svgio/svgreader/svgmasknode.hxx>
44 #include <svgio/svgreader/svgmarkernode.hxx>
45 #include <svgio/svgreader/svgpatternnode.hxx>
46 #include <svgio/svgreader/svgtitledescnode.hxx>
48 //////////////////////////////////////////////////////////////////////////////
50 using namespace com::sun::star
;
52 //////////////////////////////////////////////////////////////////////////////
56 svgio::svgreader::SvgCharacterNode
* whiteSpaceHandling(svgio::svgreader::SvgNode
* pNode
, svgio::svgreader::SvgCharacterNode
* pLast
)
60 const svgio::svgreader::SvgNodeVector
& rChilds
= pNode
->getChildren();
61 const sal_uInt32
nCount(rChilds
.size());
63 for(sal_uInt32
a(0); a
< nCount
; a
++)
65 svgio::svgreader::SvgNode
* pCandidate
= rChilds
[a
];
69 switch(pCandidate
->getType())
71 case svgio::svgreader::SVGTokenCharacter
:
73 // clean whitespace in text span
74 svgio::svgreader::SvgCharacterNode
* pCharNode
= static_cast< svgio::svgreader::SvgCharacterNode
* >(pCandidate
);
75 pCharNode
->whiteSpaceHandling();
77 // pCharNode may have lost all text. If that's the case, ignore
78 // as invalid character node
79 if(!pCharNode
->getText().isEmpty())
84 static bool bNoGapsForBaselineShift(true);
86 if(bNoGapsForBaselineShift
)
88 // With this option a baseline shift between two char parts ('words')
89 // will not add a space 'gap' to the end of the (non-last) word. This
90 // seems to be the standard behaviour, see last bugdoc attached #122524#
91 const svgio::svgreader::SvgStyleAttributes
* pStyleLast
= pLast
->getSvgStyleAttributes();
92 const svgio::svgreader::SvgStyleAttributes
* pStyleCurrent
= pCandidate
->getSvgStyleAttributes();
94 if(pStyleLast
&& pStyleCurrent
&& pStyleLast
->getBaselineShift() != pStyleCurrent
->getBaselineShift())
100 // add in-between whitespace (single space) to last
101 // known character node
108 // remember new last corected character node
113 case svgio::svgreader::SVGTokenTspan
:
114 case svgio::svgreader::SVGTokenTextPath
:
115 case svgio::svgreader::SVGTokenTref
:
117 // recursively clean whitespaces in subhierarchy
118 pLast
= whiteSpaceHandling(pCandidate
, pLast
);
123 OSL_ENSURE(false, "Unexpected token inside SVGTokenText (!)");
135 //////////////////////////////////////////////////////////////////////////////
141 SvgDocHdl::SvgDocHdl(const OUString
& aAbsolutePath
)
142 : maDocument(aAbsolutePath
),
148 SvgDocHdl::~SvgDocHdl()
153 OSL_ENSURE(false, "SvgDocHdl destructed with active target (!)");
156 OSL_ENSURE(!maCssContents
.size(), "SvgDocHdl destructed with active css style stack entry (!)");
160 void SvgDocHdl::startDocument( ) throw (xml::sax::SAXException
, uno::RuntimeException
)
162 OSL_ENSURE(!mpTarget
, "Already a target at document start (!)");
163 OSL_ENSURE(!maCssContents
.size(), "SvgDocHdl startDocument with active css style stack entry (!)");
166 void SvgDocHdl::endDocument( ) throw (xml::sax::SAXException
, uno::RuntimeException
)
168 OSL_ENSURE(!mpTarget
, "Still a target at document end (!)");
169 OSL_ENSURE(!maCssContents
.size(), "SvgDocHdl endDocument with active css style stack entry (!)");
172 void SvgDocHdl::startElement( const OUString
& aName
, const uno::Reference
< xml::sax::XAttributeList
>& xAttribs
) throw (xml::sax::SAXException
, uno::RuntimeException
)
176 const SVGToken
aSVGToken(StrToSVGToken(aName
));
180 /// structural elements
183 /// new basic node for Symbol. Content gets scanned, but
184 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
185 mpTarget
= new SvgSymbolNode(maDocument
, mpTarget
);
186 mpTarget
->parseAttributes(xAttribs
);
192 /// new node for Defs/G
193 mpTarget
= new SvgGNode(aSVGToken
, maDocument
, mpTarget
);
194 mpTarget
->parseAttributes(xAttribs
);
200 mpTarget
= new SvgSvgNode(maDocument
, mpTarget
);
201 mpTarget
->parseAttributes(xAttribs
);
207 mpTarget
= new SvgUseNode(maDocument
, mpTarget
);
208 mpTarget
->parseAttributes(xAttribs
);
215 /// new node for Circle
216 mpTarget
= new SvgCircleNode(maDocument
, mpTarget
);
217 mpTarget
->parseAttributes(xAttribs
);
220 case SVGTokenEllipse
:
222 /// new node for Ellipse
223 mpTarget
= new SvgEllipseNode(maDocument
, mpTarget
);
224 mpTarget
->parseAttributes(xAttribs
);
229 /// new node for Line
230 mpTarget
= new SvgLineNode(maDocument
, mpTarget
);
231 mpTarget
->parseAttributes(xAttribs
);
236 /// new node for Path
237 mpTarget
= new SvgPathNode(maDocument
, mpTarget
);
238 mpTarget
->parseAttributes(xAttribs
);
241 case SVGTokenPolygon
:
243 /// new node for Polygon
244 mpTarget
= new SvgPolyNode(maDocument
, mpTarget
, false);
245 mpTarget
->parseAttributes(xAttribs
);
248 case SVGTokenPolyline
:
250 /// new node for Polyline
251 mpTarget
= new SvgPolyNode(maDocument
, mpTarget
, true);
252 mpTarget
->parseAttributes(xAttribs
);
257 /// new node for Rect
258 mpTarget
= new SvgRectNode(maDocument
, mpTarget
);
259 mpTarget
->parseAttributes(xAttribs
);
264 /// new node for Image
265 mpTarget
= new SvgImageNode(maDocument
, mpTarget
);
266 mpTarget
->parseAttributes(xAttribs
);
270 /// title and description
274 /// new node for Title and/or Desc
275 mpTarget
= new SvgTitleDescNode(aSVGToken
, maDocument
, mpTarget
);
280 case SVGTokenLinearGradient
:
281 case SVGTokenRadialGradient
:
283 mpTarget
= new SvgGradientNode(aSVGToken
, maDocument
, mpTarget
);
284 mpTarget
->parseAttributes(xAttribs
);
291 mpTarget
= new SvgGradientStopNode(maDocument
, mpTarget
);
292 mpTarget
->parseAttributes(xAttribs
);
299 mpTarget
= new SvgTextNode(maDocument
, mpTarget
);
300 mpTarget
->parseAttributes(xAttribs
);
305 mpTarget
= new SvgTspanNode(maDocument
, mpTarget
);
306 mpTarget
->parseAttributes(xAttribs
);
311 mpTarget
= new SvgTrefNode(maDocument
, mpTarget
);
312 mpTarget
->parseAttributes(xAttribs
);
315 case SVGTokenTextPath
:
317 mpTarget
= new SvgTextPathNode(maDocument
, mpTarget
);
318 mpTarget
->parseAttributes(xAttribs
);
322 /// styles (as stylesheets)
325 SvgStyleNode
* pNew
= new SvgStyleNode(maDocument
, mpTarget
);
327 mpTarget
->parseAttributes(xAttribs
);
329 if(pNew
->isTextCss())
331 maCssContents
.push_back(OUString());
336 /// structural elements clip-path and mask. Content gets scanned, but
337 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
338 case SVGTokenClipPathNode
:
340 /// new node for ClipPath
341 mpTarget
= new SvgClipPathNode(maDocument
, mpTarget
);
342 mpTarget
->parseAttributes(xAttribs
);
347 /// new node for Mask
348 mpTarget
= new SvgMaskNode(maDocument
, mpTarget
);
349 mpTarget
->parseAttributes(xAttribs
);
353 /// structural element marker
356 /// new node for marker
357 mpTarget
= new SvgMarkerNode(maDocument
, mpTarget
);
358 mpTarget
->parseAttributes(xAttribs
);
362 /// structural element pattern
363 case SVGTokenPattern
:
365 /// new node for pattern
366 mpTarget
= new SvgPatternNode(maDocument
, mpTarget
);
367 mpTarget
->parseAttributes(xAttribs
);
373 /// invalid token, ignore
376 OUString::createFromAscii("Unknown Base SvgToken <") +
378 OUString::createFromAscii("> (!)"));
386 void SvgDocHdl::endElement( const OUString
& aName
) throw (xml::sax::SAXException
, uno::RuntimeException
)
390 const SVGToken
aSVGToken(StrToSVGToken(aName
));
391 SvgNode
* pWhitespaceCheck(SVGTokenText
== aSVGToken
? mpTarget
: 0);
392 SvgStyleNode
* pCssStyle(SVGTokenStyle
== aSVGToken
? static_cast< SvgStyleNode
* >(mpTarget
) : 0);
393 SvgTitleDescNode
* pSvgTitleDescNode(SVGTokenTitle
== aSVGToken
|| SVGTokenDesc
== aSVGToken
? static_cast< SvgTitleDescNode
* >(mpTarget
) : 0);
397 /// valid tokens for which a new one was created
399 /// structural elements
408 case SVGTokenEllipse
:
411 case SVGTokenPolygon
:
412 case SVGTokenPolyline
:
416 /// title and description
421 case SVGTokenLinearGradient
:
422 case SVGTokenRadialGradient
:
430 case SVGTokenTextPath
:
433 /// styles (as stylesheets)
436 /// structural elements clip-path and mask
437 case SVGTokenClipPathNode
:
440 /// structural element marker
443 /// structural element pattern
444 case SVGTokenPattern
:
446 /// content handling after parsing
450 if(!mpTarget
->getParent())
452 // last element closing, save this tree
453 maDocument
.appendNode(mpTarget
);
456 mpTarget
= const_cast< SvgNode
* >(mpTarget
->getParent());
460 OSL_ENSURE(false, "Closing token, but no context (!)");
466 /// invalid token, ignore
470 if(pSvgTitleDescNode
&& mpTarget
)
472 const OUString
aText(pSvgTitleDescNode
->getText());
476 if(SVGTokenTitle
== aSVGToken
)
478 mpTarget
->parseAttribute(getStrTitle(), aSVGToken
, aText
);
480 else // if(SVGTokenDesc == aSVGToken)
482 mpTarget
->parseAttribute(getStrDesc(), aSVGToken
, aText
);
487 if(pCssStyle
&& pCssStyle
->isTextCss())
490 if(maCssContents
.size())
492 // need to interpret css styles and remember them as StyleSheets
493 pCssStyle
->addCssStyleSheet(*(maCssContents
.end() - 1));
494 maCssContents
.pop_back();
498 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
504 // cleanup read strings
505 whiteSpaceHandling(pWhitespaceCheck
, 0);
510 void SvgDocHdl::characters( const OUString
& aChars
) throw (xml::sax::SAXException
, uno::RuntimeException
)
512 const sal_uInt32
nLength(aChars
.getLength());
514 if(mpTarget
&& nLength
)
516 switch(mpTarget
->getType())
520 case SVGTokenTextPath
:
522 const SvgNodeVector
& rChilds
= mpTarget
->getChildren();
523 SvgCharacterNode
* pTarget
= 0;
527 pTarget
= dynamic_cast< SvgCharacterNode
* >(rChilds
[rChilds
.size() - 1]);
532 // concatenate to current character span
533 pTarget
->concatenate(aChars
);
537 // add character span as simplified tspan (no arguments)
538 // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
539 new SvgCharacterNode(maDocument
, mpTarget
, aChars
);
545 SvgStyleNode
& rSvgStyleNode
= static_cast< SvgStyleNode
& >(*mpTarget
);
547 if(rSvgStyleNode
.isTextCss())
549 // collect characters for css style
550 if(maCssContents
.size())
552 const OUString
aTrimmedChars(aChars
.trim());
554 if(!aTrimmedChars
.isEmpty())
556 std::vector
< OUString
>::iterator
aString(maCssContents
.end() - 1);
557 (*aString
) += aTrimmedChars
;
562 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
570 SvgTitleDescNode
& rSvgTitleDescNode
= static_cast< SvgTitleDescNode
& >(*mpTarget
);
572 // add text directly to SvgTitleDescNode
573 rSvgTitleDescNode
.concatenate(aChars
);
578 // characters not used by a known node
585 void SvgDocHdl::ignorableWhitespace(const OUString
& /*aWhitespaces*/) throw (xml::sax::SAXException
, uno::RuntimeException
)
589 void SvgDocHdl::processingInstruction(const OUString
& /*aTarget*/, const OUString
& /*aData*/) throw (xml::sax::SAXException
, uno::RuntimeException
)
593 void SvgDocHdl::setDocumentLocator(const uno::Reference
< xml::sax::XLocator
>& /*xLocator*/) throw (xml::sax::SAXException
, uno::RuntimeException
)
596 } // end of namespace svgreader
597 } // end of namespace svgio
599 //////////////////////////////////////////////////////////////////////////////
602 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */