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