calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / svgio / source / svgreader / svgdocumenthandler.cxx
blob253374d71036c4b3dea298034c2c73ae7f76e016
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>
49 #include <osl/diagnose.h>
50 #include <o3tl/string_view.hxx>
52 using namespace com::sun::star;
54 namespace svgio::svgreader
57 namespace
59 svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgNode const * pNode, svgio::svgreader::SvgCharacterNode* pLast)
61 if(pNode)
63 const auto& rChilds = pNode->getChildren();
64 const sal_uInt32 nCount(rChilds.size());
66 for(sal_uInt32 a(0); a < nCount; a++)
68 svgio::svgreader::SvgNode* pCandidate = rChilds[a].get();
70 if(pCandidate)
72 switch(pCandidate->getType())
74 case SVGToken::Character:
76 // clean whitespace in text span
77 svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);
79 pCharNode->whiteSpaceHandling();
81 // pCharNode may have lost all text. If that's the case, ignore
82 // as invalid character node
83 // Also ignore if textBeforeSpaceHandling just have spaces
84 if(!pCharNode->getText().isEmpty() && !o3tl::trim(pCharNode->getTextBeforeSpaceHandling()).empty())
86 if(pLast)
88 bool bAddGap(true);
90 // Do not add a gap if last node doesn't end with a space and
91 // current note doesn't start with a space
92 const sal_uInt32 nLastLength(pLast->getTextBeforeSpaceHandling().getLength());
93 if(pLast->getTextBeforeSpaceHandling()[nLastLength - 1] != ' ' && pCharNode->getTextBeforeSpaceHandling()[0] != ' ')
94 bAddGap = false;
96 // With this option a baseline shift between two char parts ('words')
97 // will not add a space 'gap' to the end of the (non-last) word. This
98 // seems to be the standard behaviour, see last bugdoc attached #122524#
99 const svgio::svgreader::SvgStyleAttributes* pStyleLast = pLast->getSvgStyleAttributes();
100 const svgio::svgreader::SvgStyleAttributes* pStyleCurrent = pCandidate->getSvgStyleAttributes();
102 if(pStyleLast && pStyleCurrent && pStyleLast->getBaselineShift() != pStyleCurrent->getBaselineShift())
104 bAddGap = false;
107 // add in-between whitespace (single space) to last
108 // known character node
109 if(bAddGap)
111 pLast->addGap();
115 // remember new last corrected character node
116 pLast = pCharNode;
119 break;
121 case SVGToken::Tspan:
122 case SVGToken::TextPath:
123 case SVGToken::Tref:
125 // recursively clean whitespaces in subhierarchy
126 pLast = whiteSpaceHandling(pCandidate, pLast);
127 break;
129 default:
131 OSL_ENSURE(false, "Unexpected token inside SVGTokenText (!)");
132 break;
139 return pLast;
141 } // end anonymous namespace
143 SvgDocHdl::SvgDocHdl(const OUString& aAbsolutePath)
144 : maDocument(aAbsolutePath),
145 mpTarget(nullptr),
146 bSkip(false)
150 SvgDocHdl::~SvgDocHdl()
152 if (mpTarget)
154 OSL_ENSURE(false, "SvgDocHdl destructed with active target (!)");
156 while (mpTarget->getParent())
157 mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
159 const SvgNodeVector& rOwnedTopLevels = maDocument.getSvgNodeVector();
160 if (std::none_of(rOwnedTopLevels.begin(), rOwnedTopLevels.end(),
161 [&](std::unique_ptr<SvgNode> const & p) { return p.get() == mpTarget; }))
162 delete mpTarget;
164 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl destructed with active css style stack entry (!)");
167 void SvgDocHdl::startDocument( )
169 OSL_ENSURE(!mpTarget, "Already a target at document start (!)");
170 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl startDocument with active css style stack entry (!)");
173 void SvgDocHdl::endDocument( )
175 OSL_ENSURE(!mpTarget, "Still a target at document end (!)");
176 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl endDocument with active css style stack entry (!)");
179 void SvgDocHdl::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs )
181 if (bSkip)
182 return;
183 if(aName.isEmpty())
184 return;
186 const SVGToken aSVGToken(StrToSVGToken(aName, false));
188 switch (aSVGToken)
190 /// structural elements
191 case SVGToken::Symbol:
193 /// new basic node for Symbol. Content gets scanned, but
194 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
195 mpTarget = new SvgSymbolNode(maDocument, mpTarget);
196 mpTarget->parseAttributes(xAttribs);
197 break;
199 case SVGToken::Defs:
200 case SVGToken::G:
202 /// new node for Defs/G
203 mpTarget = new SvgGNode(aSVGToken, maDocument, mpTarget);
204 mpTarget->parseAttributes(xAttribs);
205 break;
207 case SVGToken::Svg:
209 /// new node for Svg
210 mpTarget = new SvgSvgNode(maDocument, mpTarget);
211 mpTarget->parseAttributes(xAttribs);
212 break;
214 case SVGToken::Use:
216 /// new node for Use
217 mpTarget = new SvgUseNode(maDocument, mpTarget);
218 mpTarget->parseAttributes(xAttribs);
219 break;
221 case SVGToken::A:
223 /// new node for A
224 mpTarget = new SvgANode(maDocument, mpTarget);
225 mpTarget->parseAttributes(xAttribs);
226 break;
229 /// shape elements
230 case SVGToken::Circle:
232 /// new node for Circle
233 mpTarget = new SvgCircleNode(maDocument, mpTarget);
234 mpTarget->parseAttributes(xAttribs);
235 break;
237 case SVGToken::Ellipse:
239 /// new node for Ellipse
240 mpTarget = new SvgEllipseNode(maDocument, mpTarget);
241 mpTarget->parseAttributes(xAttribs);
242 break;
244 case SVGToken::Line:
246 /// new node for Line
247 mpTarget = new SvgLineNode(maDocument, mpTarget);
248 mpTarget->parseAttributes(xAttribs);
249 break;
251 case SVGToken::Path:
253 /// new node for Path
254 mpTarget = new SvgPathNode(maDocument, mpTarget);
255 mpTarget->parseAttributes(xAttribs);
256 break;
258 case SVGToken::Polygon:
260 /// new node for Polygon
261 mpTarget = new SvgPolyNode(maDocument, mpTarget, false);
262 mpTarget->parseAttributes(xAttribs);
263 break;
265 case SVGToken::Polyline:
267 /// new node for Polyline
268 mpTarget = new SvgPolyNode(maDocument, mpTarget, true);
269 mpTarget->parseAttributes(xAttribs);
270 break;
272 case SVGToken::Rect:
274 /// new node for Rect
275 mpTarget = new SvgRectNode(maDocument, mpTarget);
276 mpTarget->parseAttributes(xAttribs);
277 break;
279 case SVGToken::Image:
281 /// new node for Image
282 mpTarget = new SvgImageNode(maDocument, mpTarget);
283 mpTarget->parseAttributes(xAttribs);
284 break;
287 /// title and description
288 case SVGToken::Title:
289 case SVGToken::Desc:
291 /// new node for Title and/or Desc
292 mpTarget = new SvgTitleDescNode(aSVGToken, maDocument, mpTarget);
293 break;
296 /// gradients
297 case SVGToken::LinearGradient:
298 case SVGToken::RadialGradient:
300 mpTarget = new SvgGradientNode(aSVGToken, maDocument, mpTarget);
301 mpTarget->parseAttributes(xAttribs);
302 break;
305 /// gradient stops
306 case SVGToken::Stop:
308 mpTarget = new SvgGradientStopNode(maDocument, mpTarget);
309 mpTarget->parseAttributes(xAttribs);
310 break;
313 /// text
314 case SVGToken::Text:
316 mpTarget = new SvgTextNode(maDocument, mpTarget);
317 mpTarget->parseAttributes(xAttribs);
318 break;
320 case SVGToken::Tspan:
322 mpTarget = new SvgTspanNode(maDocument, mpTarget);
323 mpTarget->parseAttributes(xAttribs);
324 break;
326 case SVGToken::Tref:
328 mpTarget = new SvgTrefNode(maDocument, mpTarget);
329 mpTarget->parseAttributes(xAttribs);
330 break;
332 case SVGToken::TextPath:
334 mpTarget = new SvgTextPathNode(maDocument, mpTarget);
335 mpTarget->parseAttributes(xAttribs);
336 break;
339 /// styles (as stylesheets)
340 case SVGToken::Style:
342 SvgStyleNode* pNew = new SvgStyleNode(maDocument, mpTarget);
343 mpTarget = pNew;
345 // #i125326# there are attributes, read them. This will set isTextCss to false if
346 // type attibute is different to "text/css"
347 mpTarget->parseAttributes(xAttribs);
349 if(pNew->isTextCss())
351 // if it is a Css style, allow reading text between the start and end tag (see
352 // SvgDocHdl::characters for details)
353 maCssContents.emplace_back();
355 break;
358 /// structural elements clip-path and mask. Content gets scanned, but
359 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
360 case SVGToken::ClipPathNode:
362 /// new node for ClipPath
363 mpTarget = new SvgClipPathNode(maDocument, mpTarget);
364 mpTarget->parseAttributes(xAttribs);
365 break;
367 case SVGToken::Mask:
369 /// new node for Mask
370 mpTarget = new SvgMaskNode(maDocument, mpTarget);
371 mpTarget->parseAttributes(xAttribs);
372 break;
375 /// structural element marker
376 case SVGToken::Marker:
378 /// new node for marker
379 mpTarget = new SvgMarkerNode(maDocument, mpTarget);
380 mpTarget->parseAttributes(xAttribs);
381 break;
384 /// structural element pattern
385 case SVGToken::Pattern:
387 /// new node for pattern
388 mpTarget = new SvgPatternNode(maDocument, mpTarget);
389 mpTarget->parseAttributes(xAttribs);
390 break;
393 // ignore FlowRoot and child nodes
394 case SVGToken::FlowRoot:
396 bSkip = true;
397 break;
400 default:
402 /// invalid token, ignore
403 SAL_INFO( "svgio", "Unknown Base SvgToken <" + aName + "> (!)" );
404 break;
409 void SvgDocHdl::endElement( const OUString& aName )
411 if(aName.isEmpty())
412 return;
414 const SVGToken aSVGToken(StrToSVGToken(aName, false));
415 SvgNode* pWhitespaceCheck(SVGToken::Text == aSVGToken ? mpTarget : nullptr);
416 SvgStyleNode* pCssStyle(SVGToken::Style == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : nullptr);
417 SvgTitleDescNode* pSvgTitleDescNode(SVGToken::Title == aSVGToken || SVGToken::Desc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : nullptr);
419 // if we are in skipping mode and we reach the flowRoot end tag: stop skipping mode
420 if(bSkip && aSVGToken == SVGToken::FlowRoot)
421 bSkip = false;
422 // we are in skipping mode: do nothing until we found the flowRoot end tag
423 else if(bSkip)
424 return;
426 switch (aSVGToken)
428 /// valid tokens for which a new one was created
430 /// structural elements
431 case SVGToken::Defs:
432 case SVGToken::G:
433 case SVGToken::Svg:
434 case SVGToken::Symbol:
435 case SVGToken::Use:
436 case SVGToken::A:
438 /// shape elements
439 case SVGToken::Circle:
440 case SVGToken::Ellipse:
441 case SVGToken::Line:
442 case SVGToken::Path:
443 case SVGToken::Polygon:
444 case SVGToken::Polyline:
445 case SVGToken::Rect:
446 case SVGToken::Image:
448 /// title and description
449 case SVGToken::Title:
450 case SVGToken::Desc:
452 /// gradients
453 case SVGToken::LinearGradient:
454 case SVGToken::RadialGradient:
456 /// gradient stops
457 case SVGToken::Stop:
459 /// text
460 case SVGToken::Text:
461 case SVGToken::Tspan:
462 case SVGToken::TextPath:
463 case SVGToken::Tref:
465 /// styles (as stylesheets)
466 case SVGToken::Style:
468 /// structural elements clip-path and mask
469 case SVGToken::ClipPathNode:
470 case SVGToken::Mask:
472 /// structural element marker
473 case SVGToken::Marker:
475 /// structural element pattern
476 case SVGToken::Pattern:
478 /// content handling after parsing
480 if(mpTarget)
482 if(!mpTarget->getParent())
484 // last element closing, save this tree
485 maDocument.appendNode(std::unique_ptr<SvgNode>(mpTarget));
488 mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
490 else
492 OSL_ENSURE(false, "Closing token, but no context (!)");
494 break;
496 default:
498 /// invalid token, ignore
502 if(pSvgTitleDescNode && mpTarget)
504 const OUString& aText(pSvgTitleDescNode->getText());
506 if(!aText.isEmpty())
508 if(SVGToken::Title == aSVGToken)
510 mpTarget->parseAttribute(getStrTitle(), aSVGToken, aText);
512 else // if(SVGTokenDesc == aSVGToken)
514 mpTarget->parseAttribute(getStrDesc(), aSVGToken, aText);
519 if(pCssStyle && pCssStyle->isTextCss())
521 // css style parsing
522 if(!maCssContents.empty())
524 // need to interpret css styles and remember them as StyleSheets
525 // #125325# Caution! the Css content may contain block comments
526 // (see http://www.w3.org/wiki/CSS_basics#CSS_comments). These need
527 // to be removed first
528 const OUString aCommentFreeSource(removeBlockComments(*(maCssContents.end() - 1)));
530 if(aCommentFreeSource.getLength())
532 pCssStyle->addCssStyleSheet(aCommentFreeSource);
535 maCssContents.pop_back();
537 else
539 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
543 if(pWhitespaceCheck)
545 // cleanup read strings
546 whiteSpaceHandling(pWhitespaceCheck, nullptr);
550 void SvgDocHdl::characters( const OUString& aChars )
552 const sal_uInt32 nLength(aChars.getLength());
554 if(!(mpTarget && nLength))
555 return;
557 switch(mpTarget->getType())
559 case SVGToken::Text:
560 case SVGToken::Tspan:
561 case SVGToken::TextPath:
563 const auto& rChilds = mpTarget->getChildren();
564 SvgCharacterNode* pTarget = nullptr;
566 if(!rChilds.empty())
568 pTarget = dynamic_cast< SvgCharacterNode* >(rChilds[rChilds.size() - 1].get());
571 if(pTarget)
573 // concatenate to current character span
574 pTarget->concatenate(aChars);
576 else
578 // add character span as simplified tspan (no arguments)
579 // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
580 new SvgCharacterNode(maDocument, mpTarget, aChars);
582 break;
584 case SVGToken::Style:
586 SvgStyleNode& rSvgStyleNode = static_cast< SvgStyleNode& >(*mpTarget);
588 if(rSvgStyleNode.isTextCss())
590 // collect characters for css style
591 if(!maCssContents.empty())
593 const OUString aTrimmedChars(aChars.trim());
595 if(!aTrimmedChars.isEmpty())
597 std::vector< OUString >::iterator aString(maCssContents.end() - 1);
598 (*aString) += aTrimmedChars;
601 else
603 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
606 break;
608 case SVGToken::Title:
609 case SVGToken::Desc:
611 SvgTitleDescNode& rSvgTitleDescNode = static_cast< SvgTitleDescNode& >(*mpTarget);
613 // add text directly to SvgTitleDescNode
614 rSvgTitleDescNode.concatenate(aChars);
615 break;
617 default:
619 // characters not used by a known node
620 break;
625 void SvgDocHdl::ignorableWhitespace(const OUString& /*aWhitespaces*/)
629 void SvgDocHdl::processingInstruction(const OUString& /*aTarget*/, const OUString& /*aData*/)
633 void SvgDocHdl::setDocumentLocator(const uno::Reference< xml::sax::XLocator >& /*xLocator*/)
636 } // end of namespace svgio
638 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */