Line data Source code
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 <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>
47 :
48 :
49 :
50 : using namespace com::sun::star;
51 :
52 :
53 :
54 : namespace
55 : {
56 0 : svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgNode* pNode, svgio::svgreader::SvgCharacterNode* pLast)
57 : {
58 0 : if(pNode)
59 : {
60 0 : const svgio::svgreader::SvgNodeVector& rChilds = pNode->getChildren();
61 0 : const sal_uInt32 nCount(rChilds.size());
62 :
63 0 : for(sal_uInt32 a(0); a < nCount; a++)
64 : {
65 0 : svgio::svgreader::SvgNode* pCandidate = rChilds[a];
66 :
67 0 : if(pCandidate)
68 : {
69 0 : switch(pCandidate->getType())
70 : {
71 : case svgio::svgreader::SVGTokenCharacter:
72 : {
73 : // clean whitespace in text span
74 0 : svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);
75 0 : pCharNode->whiteSpaceHandling();
76 :
77 : // pCharNode may have lost all text. If that's the case, ignore
78 : // as invalid character node
79 0 : if(!pCharNode->getText().isEmpty())
80 : {
81 0 : if(pLast)
82 : {
83 0 : bool bAddGap(true);
84 : static bool bNoGapsForBaselineShift(true);
85 :
86 0 : if(bNoGapsForBaselineShift)
87 : {
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 0 : const svgio::svgreader::SvgStyleAttributes* pStyleLast = pLast->getSvgStyleAttributes();
92 0 : const svgio::svgreader::SvgStyleAttributes* pStyleCurrent = pCandidate->getSvgStyleAttributes();
93 :
94 0 : if(pStyleLast && pStyleCurrent && pStyleLast->getBaselineShift() != pStyleCurrent->getBaselineShift())
95 : {
96 0 : bAddGap = false;
97 : }
98 : }
99 :
100 : // add in-between whitespace (single space) to last
101 : // known character node
102 0 : if(bAddGap)
103 : {
104 0 : pLast->addGap();
105 : }
106 : }
107 :
108 : // remember new last corected character node
109 0 : pLast = pCharNode;
110 : }
111 0 : break;
112 : }
113 : case svgio::svgreader::SVGTokenTspan:
114 : case svgio::svgreader::SVGTokenTextPath:
115 : case svgio::svgreader::SVGTokenTref:
116 : {
117 : // recursively clean whitespaces in subhierarchy
118 0 : pLast = whiteSpaceHandling(pCandidate, pLast);
119 0 : break;
120 : }
121 : default:
122 : {
123 : OSL_ENSURE(false, "Unexpected token inside SVGTokenText (!)");
124 0 : break;
125 : }
126 : }
127 : }
128 : }
129 : }
130 :
131 0 : return pLast;
132 : }
133 : }
134 :
135 :
136 :
137 : namespace svgio
138 : {
139 : namespace svgreader
140 : {
141 0 : SvgDocHdl::SvgDocHdl(const OUString& aAbsolutePath)
142 : : maDocument(aAbsolutePath),
143 : mpTarget(0),
144 0 : maCssContents()
145 : {
146 0 : }
147 :
148 0 : SvgDocHdl::~SvgDocHdl()
149 : {
150 : #ifdef DBG_UTIL
151 : if(mpTarget)
152 : {
153 : OSL_ENSURE(false, "SvgDocHdl destructed with active target (!)");
154 : delete mpTarget;
155 : }
156 : OSL_ENSURE(!maCssContents.size(), "SvgDocHdl destructed with active css style stack entry (!)");
157 : #endif
158 0 : }
159 :
160 0 : void SvgDocHdl::startDocument( ) throw (xml::sax::SAXException, uno::RuntimeException, std::exception)
161 : {
162 : OSL_ENSURE(!mpTarget, "Already a target at document start (!)");
163 : OSL_ENSURE(!maCssContents.size(), "SvgDocHdl startDocument with active css style stack entry (!)");
164 0 : }
165 :
166 0 : void SvgDocHdl::endDocument( ) throw (xml::sax::SAXException, uno::RuntimeException, std::exception)
167 : {
168 : OSL_ENSURE(!mpTarget, "Still a target at document end (!)");
169 : OSL_ENSURE(!maCssContents.size(), "SvgDocHdl endDocument with active css style stack entry (!)");
170 0 : }
171 :
172 0 : void SvgDocHdl::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs ) throw (xml::sax::SAXException, uno::RuntimeException, std::exception)
173 : {
174 0 : if(!aName.isEmpty())
175 : {
176 0 : const SVGToken aSVGToken(StrToSVGToken(aName));
177 :
178 0 : switch(aSVGToken)
179 : {
180 : /// structural elements
181 : case SVGTokenSymbol:
182 : {
183 : /// new basic node for Symbol. Content gets scanned, but
184 : /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
185 0 : mpTarget = new SvgSymbolNode(maDocument, mpTarget);
186 0 : mpTarget->parseAttributes(xAttribs);
187 0 : break;
188 : }
189 : case SVGTokenDefs:
190 : case SVGTokenG:
191 : {
192 : /// new node for Defs/G
193 0 : mpTarget = new SvgGNode(aSVGToken, maDocument, mpTarget);
194 0 : mpTarget->parseAttributes(xAttribs);
195 0 : break;
196 : }
197 : case SVGTokenSvg:
198 : {
199 : /// new node for Svg
200 0 : mpTarget = new SvgSvgNode(maDocument, mpTarget);
201 0 : mpTarget->parseAttributes(xAttribs);
202 0 : break;
203 : }
204 : case SVGTokenUse:
205 : {
206 : /// new node for Use
207 0 : mpTarget = new SvgUseNode(maDocument, mpTarget);
208 0 : mpTarget->parseAttributes(xAttribs);
209 0 : break;
210 : }
211 :
212 : /// shape elements
213 : case SVGTokenCircle:
214 : {
215 : /// new node for Circle
216 0 : mpTarget = new SvgCircleNode(maDocument, mpTarget);
217 0 : mpTarget->parseAttributes(xAttribs);
218 0 : break;
219 : }
220 : case SVGTokenEllipse:
221 : {
222 : /// new node for Ellipse
223 0 : mpTarget = new SvgEllipseNode(maDocument, mpTarget);
224 0 : mpTarget->parseAttributes(xAttribs);
225 0 : break;
226 : }
227 : case SVGTokenLine:
228 : {
229 : /// new node for Line
230 0 : mpTarget = new SvgLineNode(maDocument, mpTarget);
231 0 : mpTarget->parseAttributes(xAttribs);
232 0 : break;
233 : }
234 : case SVGTokenPath:
235 : {
236 : /// new node for Path
237 0 : mpTarget = new SvgPathNode(maDocument, mpTarget);
238 0 : mpTarget->parseAttributes(xAttribs);
239 0 : break;
240 : }
241 : case SVGTokenPolygon:
242 : {
243 : /// new node for Polygon
244 0 : mpTarget = new SvgPolyNode(maDocument, mpTarget, false);
245 0 : mpTarget->parseAttributes(xAttribs);
246 0 : break;
247 : }
248 : case SVGTokenPolyline:
249 : {
250 : /// new node for Polyline
251 0 : mpTarget = new SvgPolyNode(maDocument, mpTarget, true);
252 0 : mpTarget->parseAttributes(xAttribs);
253 0 : break;
254 : }
255 : case SVGTokenRect:
256 : {
257 : /// new node for Rect
258 0 : mpTarget = new SvgRectNode(maDocument, mpTarget);
259 0 : mpTarget->parseAttributes(xAttribs);
260 0 : break;
261 : }
262 : case SVGTokenImage:
263 : {
264 : /// new node for Image
265 0 : mpTarget = new SvgImageNode(maDocument, mpTarget);
266 0 : mpTarget->parseAttributes(xAttribs);
267 0 : break;
268 : }
269 :
270 : /// title and description
271 : case SVGTokenTitle:
272 : case SVGTokenDesc:
273 : {
274 : /// new node for Title and/or Desc
275 0 : mpTarget = new SvgTitleDescNode(aSVGToken, maDocument, mpTarget);
276 0 : break;
277 : }
278 :
279 : /// gradients
280 : case SVGTokenLinearGradient:
281 : case SVGTokenRadialGradient:
282 : {
283 0 : mpTarget = new SvgGradientNode(aSVGToken, maDocument, mpTarget);
284 0 : mpTarget->parseAttributes(xAttribs);
285 0 : break;
286 : }
287 :
288 : /// gradient stops
289 : case SVGTokenStop:
290 : {
291 0 : mpTarget = new SvgGradientStopNode(maDocument, mpTarget);
292 0 : mpTarget->parseAttributes(xAttribs);
293 0 : break;
294 : }
295 :
296 : /// text
297 : case SVGTokenText:
298 : {
299 0 : mpTarget = new SvgTextNode(maDocument, mpTarget);
300 0 : mpTarget->parseAttributes(xAttribs);
301 0 : break;
302 : }
303 : case SVGTokenTspan:
304 : {
305 0 : mpTarget = new SvgTspanNode(maDocument, mpTarget);
306 0 : mpTarget->parseAttributes(xAttribs);
307 0 : break;
308 : }
309 : case SVGTokenTref:
310 : {
311 0 : mpTarget = new SvgTrefNode(maDocument, mpTarget);
312 0 : mpTarget->parseAttributes(xAttribs);
313 0 : break;
314 : }
315 : case SVGTokenTextPath:
316 : {
317 0 : mpTarget = new SvgTextPathNode(maDocument, mpTarget);
318 0 : mpTarget->parseAttributes(xAttribs);
319 0 : break;
320 : }
321 :
322 : /// styles (as stylesheets)
323 : case SVGTokenStyle:
324 : {
325 0 : SvgStyleNode* pNew = new SvgStyleNode(maDocument, mpTarget);
326 0 : mpTarget = pNew;
327 0 : mpTarget->parseAttributes(xAttribs);
328 :
329 0 : if(pNew->isTextCss())
330 : {
331 0 : maCssContents.push_back(OUString());
332 : }
333 0 : break;
334 : }
335 :
336 : /// structural elements clip-path and mask. Content gets scanned, but
337 : /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
338 : case SVGTokenClipPathNode:
339 : {
340 : /// new node for ClipPath
341 0 : mpTarget = new SvgClipPathNode(maDocument, mpTarget);
342 0 : mpTarget->parseAttributes(xAttribs);
343 0 : break;
344 : }
345 : case SVGTokenMask:
346 : {
347 : /// new node for Mask
348 0 : mpTarget = new SvgMaskNode(maDocument, mpTarget);
349 0 : mpTarget->parseAttributes(xAttribs);
350 0 : break;
351 : }
352 :
353 : /// structural element marker
354 : case SVGTokenMarker:
355 : {
356 : /// new node for marker
357 0 : mpTarget = new SvgMarkerNode(maDocument, mpTarget);
358 0 : mpTarget->parseAttributes(xAttribs);
359 0 : break;
360 : }
361 :
362 : /// structural element pattern
363 : case SVGTokenPattern:
364 : {
365 : /// new node for pattern
366 0 : mpTarget = new SvgPatternNode(maDocument, mpTarget);
367 0 : mpTarget->parseAttributes(xAttribs);
368 0 : break;
369 : }
370 :
371 : default:
372 : {
373 : /// invalid token, ignore
374 : #ifdef DBG_UTIL
375 : myAssert(
376 : OUString("Unknown Base SvgToken <") +
377 : aName +
378 : OUString("> (!)") );
379 : #endif
380 0 : break;
381 : }
382 : }
383 : }
384 0 : }
385 :
386 0 : void SvgDocHdl::endElement( const OUString& aName ) throw (xml::sax::SAXException, uno::RuntimeException, std::exception)
387 : {
388 0 : if(!aName.isEmpty())
389 : {
390 0 : const SVGToken aSVGToken(StrToSVGToken(aName));
391 0 : SvgNode* pWhitespaceCheck(SVGTokenText == aSVGToken ? mpTarget : 0);
392 0 : SvgStyleNode* pCssStyle(SVGTokenStyle == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : 0);
393 0 : SvgTitleDescNode* pSvgTitleDescNode(SVGTokenTitle == aSVGToken || SVGTokenDesc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : 0);
394 :
395 0 : switch(aSVGToken)
396 : {
397 : /// valid tokens for which a new one was created
398 :
399 : /// structural elements
400 : case SVGTokenDefs:
401 : case SVGTokenG:
402 : case SVGTokenSvg:
403 : case SVGTokenSymbol:
404 : case SVGTokenUse:
405 :
406 : /// shape elements
407 : case SVGTokenCircle:
408 : case SVGTokenEllipse:
409 : case SVGTokenLine:
410 : case SVGTokenPath:
411 : case SVGTokenPolygon:
412 : case SVGTokenPolyline:
413 : case SVGTokenRect:
414 : case SVGTokenImage:
415 :
416 : /// title and description
417 : case SVGTokenTitle:
418 : case SVGTokenDesc:
419 :
420 : /// gradients
421 : case SVGTokenLinearGradient:
422 : case SVGTokenRadialGradient:
423 :
424 : /// gradient stops
425 : case SVGTokenStop:
426 :
427 : /// text
428 : case SVGTokenText:
429 : case SVGTokenTspan:
430 : case SVGTokenTextPath:
431 : case SVGTokenTref:
432 :
433 : /// styles (as stylesheets)
434 : case SVGTokenStyle:
435 :
436 : /// structural elements clip-path and mask
437 : case SVGTokenClipPathNode:
438 : case SVGTokenMask:
439 :
440 : /// structural element marker
441 : case SVGTokenMarker:
442 :
443 : /// structural element pattern
444 : case SVGTokenPattern:
445 :
446 : /// content handling after parsing
447 : {
448 0 : if(mpTarget)
449 : {
450 0 : if(!mpTarget->getParent())
451 : {
452 : // last element closing, save this tree
453 0 : maDocument.appendNode(mpTarget);
454 : }
455 :
456 0 : mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
457 : }
458 : else
459 : {
460 : OSL_ENSURE(false, "Closing token, but no context (!)");
461 : }
462 0 : break;
463 : }
464 : default:
465 : {
466 : /// invalid token, ignore
467 : }
468 : }
469 :
470 0 : if(pSvgTitleDescNode && mpTarget)
471 : {
472 0 : const OUString aText(pSvgTitleDescNode->getText());
473 :
474 0 : if(!aText.isEmpty())
475 : {
476 0 : if(SVGTokenTitle == aSVGToken)
477 : {
478 0 : mpTarget->parseAttribute(getStrTitle(), aSVGToken, aText);
479 : }
480 : else // if(SVGTokenDesc == aSVGToken)
481 : {
482 0 : mpTarget->parseAttribute(getStrDesc(), aSVGToken, aText);
483 : }
484 0 : }
485 : }
486 :
487 0 : if(pCssStyle && pCssStyle->isTextCss())
488 : {
489 : // css style parsing
490 0 : if(maCssContents.size())
491 : {
492 : // need to interpret css styles and remember them as StyleSheets
493 0 : pCssStyle->addCssStyleSheet(*(maCssContents.end() - 1));
494 0 : maCssContents.pop_back();
495 : }
496 : else
497 : {
498 : OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
499 : }
500 : }
501 :
502 0 : if(pWhitespaceCheck)
503 : {
504 : // cleanup read strings
505 0 : whiteSpaceHandling(pWhitespaceCheck, 0);
506 : }
507 : }
508 0 : }
509 :
510 0 : void SvgDocHdl::characters( const OUString& aChars ) throw (xml::sax::SAXException, uno::RuntimeException, std::exception)
511 : {
512 0 : const sal_uInt32 nLength(aChars.getLength());
513 :
514 0 : if(mpTarget && nLength)
515 : {
516 0 : switch(mpTarget->getType())
517 : {
518 : case SVGTokenText:
519 : case SVGTokenTspan:
520 : case SVGTokenTextPath:
521 : {
522 0 : const SvgNodeVector& rChilds = mpTarget->getChildren();
523 0 : SvgCharacterNode* pTarget = 0;
524 :
525 0 : if(rChilds.size())
526 : {
527 0 : pTarget = dynamic_cast< SvgCharacterNode* >(rChilds[rChilds.size() - 1]);
528 : }
529 :
530 0 : if(pTarget)
531 : {
532 : // concatenate to current character span
533 0 : pTarget->concatenate(aChars);
534 : }
535 : else
536 : {
537 : // add character span as simplified tspan (no arguments)
538 : // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
539 0 : new SvgCharacterNode(maDocument, mpTarget, aChars);
540 : }
541 0 : break;
542 : }
543 : case SVGTokenStyle:
544 : {
545 0 : SvgStyleNode& rSvgStyleNode = static_cast< SvgStyleNode& >(*mpTarget);
546 :
547 0 : if(rSvgStyleNode.isTextCss())
548 : {
549 : // collect characters for css style
550 0 : if(maCssContents.size())
551 : {
552 0 : const OUString aTrimmedChars(aChars.trim());
553 :
554 0 : if(!aTrimmedChars.isEmpty())
555 : {
556 0 : std::vector< OUString >::iterator aString(maCssContents.end() - 1);
557 0 : (*aString) += aTrimmedChars;
558 0 : }
559 : }
560 : else
561 : {
562 : OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
563 : }
564 : }
565 0 : break;
566 : }
567 : case SVGTokenTitle:
568 : case SVGTokenDesc:
569 : {
570 0 : SvgTitleDescNode& rSvgTitleDescNode = static_cast< SvgTitleDescNode& >(*mpTarget);
571 :
572 : // add text directly to SvgTitleDescNode
573 0 : rSvgTitleDescNode.concatenate(aChars);
574 0 : break;
575 : }
576 : default:
577 : {
578 : // characters not used by a known node
579 0 : break;
580 : }
581 : }
582 : }
583 0 : }
584 :
585 0 : void SvgDocHdl::ignorableWhitespace(const OUString& /*aWhitespaces*/) throw (xml::sax::SAXException, uno::RuntimeException, std::exception)
586 : {
587 0 : }
588 :
589 0 : void SvgDocHdl::processingInstruction(const OUString& /*aTarget*/, const OUString& /*aData*/) throw (xml::sax::SAXException, uno::RuntimeException, std::exception)
590 : {
591 0 : }
592 :
593 0 : void SvgDocHdl::setDocumentLocator(const uno::Reference< xml::sax::XLocator >& /*xLocator*/) throw (xml::sax::SAXException, uno::RuntimeException, std::exception)
594 : {
595 0 : }
596 : } // end of namespace svgreader
597 : } // end of namespace svgio
598 :
599 :
600 : // eof
601 :
602 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|