1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_svgio.hxx"
24 
25 #include <svgio/svgreader/svgtools.hxx>
26 #include <osl/thread.h>
27 #include <tools/color.hxx>
28 #include <basegfx/matrix/b2dhommatrix.hxx>
29 #include <basegfx/matrix/b2dhommatrixtools.hxx>
30 #include <svgio/svgreader/svgtoken.hxx>
31 #include <hash_map>
32 
33 //////////////////////////////////////////////////////////////////////////////
34 
35 namespace svgio
36 {
37     namespace svgreader
38     {
39 #ifdef DBG_UTIL
40         void myAssert(const rtl::OUString& rMessage)
41         {
42             rtl::OString aMessage2;
43 
44             rMessage.convertToString(&aMessage2, osl_getThreadTextEncoding(), RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR|RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR);
45             OSL_ENSURE(false, aMessage2.getStr());
46         }
47 #endif
48 
49         // common non-token strings
50         const rtl::OUString commonStrings::aStrUserSpaceOnUse(rtl::OUString::createFromAscii("userSpaceOnUse"));
51         const rtl::OUString commonStrings::aStrObjectBoundingBox(rtl::OUString::createFromAscii("objectBoundingBox"));
52         const rtl::OUString commonStrings::aStrNonzero(rtl::OUString::createFromAscii("nonzero"));
53         const rtl::OUString commonStrings::aStrEvenOdd(rtl::OUString::createFromAscii("evenodd"));
54 
55         basegfx::B2DHomMatrix SvgAspectRatio::createLinearMapping(const basegfx::B2DRange& rTarget, const basegfx::B2DRange& rSource)
56         {
57             basegfx::B2DHomMatrix aRetval;
58             const double fSWidth(rSource.getWidth());
59             const double fSHeight(rSource.getHeight());
60             const bool bNoSWidth(basegfx::fTools::equalZero(fSWidth));
61             const bool bNoSHeight(basegfx::fTools::equalZero(fSHeight));
62 
63             // transform from source state to unit range
64             aRetval.translate(-rSource.getMinX(), -rSource.getMinY());
65             aRetval.scale(
66                 (bNoSWidth ? 1.0 : 1.0 / fSWidth) * rTarget.getWidth(),
67                 (bNoSHeight ? 1.0 : 1.0 / fSHeight) * rTarget.getHeight());
68 
69             // transform from unit rage to target range
70             aRetval.translate(rTarget.getMinX(), rTarget.getMinY());
71 
72             return aRetval;
73         }
74 
75         basegfx::B2DHomMatrix SvgAspectRatio::createMapping(const basegfx::B2DRange& rTarget, const basegfx::B2DRange& rSource) const
76         {
77             if(!isSet() || Align_none == getSvgAlign())
78             {
79                 // create linear mapping (default)
80                 return createLinearMapping(rTarget, rSource);
81             }
82 
83             basegfx::B2DHomMatrix aRetval;
84 
85             const double fSWidth(rSource.getWidth());
86             const double fSHeight(rSource.getHeight());
87             const bool bNoSWidth(basegfx::fTools::equalZero(fSWidth));
88             const bool bNoSHeight(basegfx::fTools::equalZero(fSHeight));
89             const double fScaleX((bNoSWidth ? 1.0 : 1.0 / fSWidth) * rTarget.getWidth());
90             const double fScaleY((bNoSHeight ? 1.0 : 1.0 / fSHeight) * rTarget.getHeight());
91             const double fScale(isMeetOrSlice() ? std::min(fScaleX, fScaleY) : std::max(fScaleX, fScaleY));
92 
93             // remove source translation, apply scale
94             aRetval.translate(-rSource.getMinX(), -rSource.getMinY());
95             aRetval.scale(fScale, fScale);
96 
97             // evaluate horizontal alignment
98             const double fNewWidth(fSWidth * fScale);
99             double fTransX(0.0);
100 
101             switch(getSvgAlign())
102             {
103                 case Align_xMidYMin:
104                 case Align_xMidYMid:
105                 case Align_xMidYMax:
106                 {
107                     // centerX
108                     const double fFreeSpace(rTarget.getWidth() - fNewWidth);
109                     fTransX = fFreeSpace * 0.5;
110                     break;
111                 }
112                 case Align_xMaxYMin:
113                 case Align_xMaxYMid:
114                 case Align_xMaxYMax:
115                 {
116                     // Right align
117                     const double fFreeSpace(rTarget.getWidth() - fNewWidth);
118                     fTransX = fFreeSpace;
119                     break;
120                 }
121             }
122 
123             // evaluate vertical alignment
124             const double fNewHeight(fSHeight * fScale);
125             double fTransY(0.0);
126 
127             switch(getSvgAlign())
128             {
129                 case Align_xMinYMid:
130                 case Align_xMidYMid:
131                 case Align_xMaxYMid:
132                 {
133                     // centerY
134                     const double fFreeSpace(rTarget.getHeight() - fNewHeight);
135                     fTransY = fFreeSpace * 0.5;
136                     break;
137                 }
138                 case Align_xMinYMax:
139                 case Align_xMidYMax:
140                 case Align_xMaxYMax:
141                 {
142                     // Bottom align
143                     const double fFreeSpace(rTarget.getHeight() - fNewHeight);
144                     fTransY = fFreeSpace;
145                     break;
146                 }
147             }
148 
149             // add target translation
150             aRetval.translate(
151                 rTarget.getMinX() + fTransX,
152                 rTarget.getMinY() + fTransY);
153 
154             return aRetval;
155         }
156 
157         double SvgNumber::solve(const InfoProvider& rInfoProvider, NumberType aNumberType) const
158         {
159             if(isSet())
160             {
161                 switch(meUnit)
162                 {
163                     case Unit_em:
164                     {
165                         return mfNumber * rInfoProvider.getCurrentFontSize();
166                         break;
167                     }
168                     case Unit_ex:
169                     {
170                         return mfNumber * rInfoProvider.getCurrentXHeight() * 0.5;
171                         break;
172                     }
173                     case Unit_px:
174                     {
175                         return mfNumber;
176                         break;
177                     }
178                     case Unit_pt:
179                     case Unit_pc:
180                     case Unit_cm:
181                     case Unit_mm:
182                     case Unit_in:
183                     {
184                         double fRetval(mfNumber);
185 
186                         switch(meUnit)
187                         {
188                             case Unit_pt: fRetval *= 1.25; break;
189                             case Unit_pc: fRetval *= 15.0; break;
190                             case Unit_cm: fRetval *= 35.43307; break;
191                             case Unit_mm: fRetval *= 3.543307; break;
192                             case Unit_in: fRetval *= 90.0; break;
193                         }
194 
195                         return fRetval;
196                         break;
197                     }
198                     case Unit_percent:
199                     {
200                         double fRetval(mfNumber * 0.01);
201                         const basegfx::B2DRange* pViewPort = rInfoProvider.getCurrentViewPort();
202 
203                         if(!pViewPort)
204                         {
205                             // no viewPort, assume a normal page size (A4)
206                             static basegfx::B2DRange aDinA4Range(
207                                 0.0,
208                                 0.0,
209                                 210.0 * 3.543307,
210                                 297.0 * 3.543307);
211 
212                             pViewPort = &aDinA4Range;
213                         }
214 
215                         if(pViewPort)
216                         {
217                             if(xcoordinate == aNumberType)
218                             {
219                                 // it's a x-coordinate, relative to current width (w)
220                                 fRetval *= pViewPort->getWidth();
221                             }
222                             else if(ycoordinate == aNumberType)
223                             {
224                                 // it's a y-coordinate, relative to current height (h)
225                                 fRetval *= pViewPort->getHeight();
226                             }
227                             else // length
228                             {
229                                 // it's a length, relative to sqrt(w*w + h*h)/sqrt(2)
230                                 const double fCurrentWidth(pViewPort->getWidth());
231                                 const double fCurrentHeight(pViewPort->getHeight());
232                                 const double fCurrentLength(
233                                     sqrt(fCurrentWidth * fCurrentWidth + fCurrentHeight * fCurrentHeight)/sqrt(2.0));
234 
235                                 fRetval *= fCurrentLength;
236                             }
237                         }
238 
239                         return fRetval;
240                         break;
241                     }
242                 }
243             }
244 
245             /// not set
246             OSL_ENSURE(false, "SvgNumber not set (!)");
247             return 0.0;
248         }
249 
250         bool SvgNumber::isPositive() const
251         {
252             return basegfx::fTools::moreOrEqual(mfNumber, 0.0);
253         }
254 
255         void skip_char(const rtl::OUString& rCandidate, const sal_Unicode& rChar, sal_Int32& nPos, const sal_Int32 nLen)
256         {
257             while(nPos < nLen && rChar == rCandidate[nPos])
258             {
259                 nPos++;
260             }
261         }
262 
263         void skip_char(const rtl::OUString& rCandidate, const sal_Unicode& rCharA, const sal_Unicode& rCharB, sal_Int32& nPos, const sal_Int32 nLen)
264         {
265             while(nPos < nLen && (rCharA == rCandidate[nPos] || rCharB == rCandidate[nPos]))
266             {
267                 nPos++;
268             }
269         }
270 
271         void copySign(const rtl::OUString& rCandidate, sal_Int32& nPos, rtl::OUStringBuffer& rTarget, const sal_Int32 nLen)
272         {
273             if(nPos < nLen)
274             {
275                 const sal_Unicode aChar(rCandidate[nPos]);
276 
277                 if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
278                 {
279                     rTarget.append(aChar);
280                     nPos++;
281                 }
282             }
283         }
284 
285         void copyNumber(const rtl::OUString& rCandidate, sal_Int32& nPos, rtl::OUStringBuffer& rTarget, const sal_Int32 nLen)
286         {
287             bool bOnNumber(true);
288 
289             while(bOnNumber && nPos < nLen)
290             {
291                 const sal_Unicode aChar(rCandidate[nPos]);
292 
293                 bOnNumber = (sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar) || sal_Unicode('.') == aChar;
294 
295                 if(bOnNumber)
296                 {
297                     rTarget.append(aChar);
298                     nPos++;
299                 }
300             }
301         }
302 
303         void copyHex(const rtl::OUString& rCandidate, sal_Int32& nPos, rtl::OUStringBuffer& rTarget, const sal_Int32 nLen)
304         {
305             bool bOnHex(true);
306 
307             while(bOnHex && nPos < nLen)
308             {
309                 const sal_Unicode aChar(rCandidate[nPos]);
310 
311                 bOnHex = (sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
312                     || (sal_Unicode('A') <= aChar && sal_Unicode('F') >= aChar)
313                     || (sal_Unicode('a') <= aChar && sal_Unicode('f') >= aChar);
314 
315                 if(bOnHex)
316                 {
317                     rTarget.append(aChar);
318                     nPos++;
319                 }
320             }
321         }
322 
323         void copyString(const rtl::OUString& rCandidate, sal_Int32& nPos, rtl::OUStringBuffer& rTarget, const sal_Int32 nLen)
324         {
325             bool bOnChar(true);
326 
327             while(bOnChar && nPos < nLen)
328             {
329                 const sal_Unicode aChar(rCandidate[nPos]);
330 
331                 bOnChar = (sal_Unicode('a') <= aChar && sal_Unicode('z') >= aChar)
332                     || (sal_Unicode('A') <= aChar && sal_Unicode('Z') >= aChar)
333                     || sal_Unicode('-') == aChar;
334 
335                 if(bOnChar)
336                 {
337                     rTarget.append(aChar);
338                     nPos++;
339                 }
340             }
341         }
342 
343         void copyToLimiter(const rtl::OUString& rCandidate, const sal_Unicode& rLimiter, sal_Int32& nPos, rtl::OUStringBuffer& rTarget, const sal_Int32 nLen)
344         {
345             while(nPos < nLen && rLimiter != rCandidate[nPos])
346             {
347                 rTarget.append(rCandidate[nPos]);
348                 nPos++;
349             }
350         }
351 
352         bool readNumber(const rtl::OUString& rCandidate, sal_Int32& nPos, double& fNum, const sal_Int32 nLen)
353         {
354             if(nPos < nLen)
355             {
356                 rtl::OUStringBuffer aNum;
357 
358                 copySign(rCandidate, nPos, aNum, nLen);
359                 copyNumber(rCandidate, nPos, aNum, nLen);
360 
361                 if(nPos < nLen)
362                 {
363                     const sal_Unicode aChar(rCandidate[nPos]);
364 
365                     if(sal_Unicode('e') == aChar || sal_Unicode('E') == aChar)
366                     {
367                         // try to read exponential number, but be careful. I had
368                         // a case where dx="2em" was used, thus the 'e' was consumed
369                         // by error. First try if there are numbers after the 'e',
370                         // safe current state
371                         nPos++;
372                         const rtl::OUStringBuffer aNum2(aNum);
373                         const sal_Int32 nPosAfterE(nPos);
374 
375                         aNum.append(aChar);
376                         copySign(rCandidate, nPos, aNum, nLen);
377                         copyNumber(rCandidate, nPos, aNum, nLen);
378 
379                         if(nPosAfterE == nPos)
380                         {
381                             // no number after 'e', go back. Do not
382                             // return false, it's still a valid integer number
383                             aNum = aNum2;
384                             nPos--;
385                         }
386                     }
387                 }
388 
389                 if(aNum.getLength())
390                 {
391                     rtl_math_ConversionStatus eStatus;
392 
393                     fNum = rtl::math::stringToDouble(
394                         aNum.makeStringAndClear(), (sal_Unicode)('.'), (sal_Unicode)(','),
395                         &eStatus, 0);
396 
397                     return eStatus == rtl_math_ConversionStatus_Ok;
398                 }
399             }
400 
401             return false;
402         }
403 
404         SvgUnit readUnit(const rtl::OUString& rCandidate, sal_Int32& nPos, const sal_Int32 nLen)
405         {
406             SvgUnit aRetval(Unit_px);
407 
408             if(nPos < nLen)
409             {
410                 const sal_Unicode aCharA(rCandidate[nPos]);
411 
412                 if(nPos + 1 < nLen)
413                 {
414                     const sal_Unicode aCharB(rCandidate[nPos + 1]);
415                     bool bTwoCharValid(false);
416 
417                     switch(aCharA)
418                     {
419                         case sal_Unicode('e') :
420                         {
421                             if(sal_Unicode('m') == aCharB)
422                             {
423                                 // 'em' Relative to current font size
424                                 aRetval = Unit_em;
425                                 bTwoCharValid = true;
426                             }
427                             else if(sal_Unicode('x') == aCharB)
428                             {
429                                 // 'ex' Relative to current font x-height
430                                 aRetval = Unit_ex;
431                                 bTwoCharValid = true;
432                             }
433                             break;
434                         }
435                         case sal_Unicode('p') :
436                         {
437                             if(sal_Unicode('x') == aCharB)
438                             {
439                                 // 'px' UserUnit (default)
440                                 bTwoCharValid = true;
441                             }
442                             else if(sal_Unicode('t') == aCharB)
443                             {
444                                 // 'pt' == 1.25 px
445                                 aRetval = Unit_pt;
446                                 bTwoCharValid = true;
447                             }
448                             else if(sal_Unicode('c') == aCharB)
449                             {
450                                 // 'pc' == 15 px
451                                 aRetval = Unit_pc;
452                                 bTwoCharValid = true;
453                             }
454                             break;
455                         }
456                         case sal_Unicode('i') :
457                         {
458                             if(sal_Unicode('n') == aCharB)
459                             {
460                                 // 'in' == 90 px
461                                 aRetval = Unit_in;
462                                 bTwoCharValid = true;
463                             }
464                             break;
465                         }
466                         case sal_Unicode('c') :
467                         {
468                             if(sal_Unicode('m') == aCharB)
469                             {
470                                 // 'cm' == 35.43307 px
471                                 aRetval = Unit_cm;
472                                 bTwoCharValid = true;
473                             }
474                             break;
475                         }
476                         case sal_Unicode('m') :
477                         {
478                             if(sal_Unicode('m') == aCharB)
479                             {
480                                 // 'mm' == 3.543307 px
481                                 aRetval = Unit_mm;
482                                 bTwoCharValid = true;
483                             }
484                             break;
485                         }
486                     }
487 
488                     if(bTwoCharValid)
489                     {
490                         nPos += 2;
491                     }
492                 }
493                 else
494                 {
495                     if(sal_Unicode('%') == aCharA)
496                     {
497                         // percent used, relative to current
498                         nPos++;
499                         aRetval = Unit_percent;
500                     }
501                 }
502             }
503 
504             return aRetval;
505         }
506 
507         bool readNumberAndUnit(const rtl::OUString& rCandidate, sal_Int32& nPos, SvgNumber& aNum, const sal_Int32 nLen)
508         {
509             double fNum(0.0);
510 
511             if(readNumber(rCandidate, nPos, fNum, nLen))
512             {
513                 skip_char(rCandidate, sal_Unicode(' '), nPos, nLen);
514                 aNum = SvgNumber(fNum, readUnit(rCandidate, nPos, nLen));
515 
516                 return true;
517             }
518 
519             return false;
520         }
521 
522         bool readAngle(const rtl::OUString& rCandidate, sal_Int32& nPos, double& fAngle, const sal_Int32 nLen)
523         {
524             if(readNumber(rCandidate, nPos, fAngle, nLen))
525             {
526                 skip_char(rCandidate, sal_Unicode(' '), nPos, nLen);
527 
528                 enum DegreeType
529                 {
530                     deg,
531                     grad,
532                     rad
533                 } aType(deg); // degrees is default
534 
535                 if(nPos < nLen)
536                 {
537                     const sal_Unicode aChar(rCandidate[nPos]);
538                     static rtl::OUString aStrGrad(rtl::OUString::createFromAscii("grad"));
539                     static rtl::OUString aStrRad(rtl::OUString::createFromAscii("rad"));
540 
541                     switch(aChar)
542                     {
543                         case sal_Unicode('g') :
544                         case sal_Unicode('G') :
545                         {
546                             if(rCandidate.matchIgnoreAsciiCase(aStrGrad, nPos))
547                             {
548                                 // angle in grad
549                                 nPos += aStrGrad.getLength();
550                             }
551                             break;
552                         }
553                         case sal_Unicode('r') :
554                         case sal_Unicode('R') :
555                         {
556                             if(rCandidate.matchIgnoreAsciiCase(aStrRad, nPos))
557                             {
558                                 // angle in radians
559                                 nPos += aStrRad.getLength();
560                             }
561                             break;
562                         }
563                     }
564                 }
565 
566                 // convert to radians
567                 if(deg == aType)
568                 {
569                     fAngle *= F_PI / 180.0;
570                 }
571                 else if(grad == aType)
572                 {
573                     // looks like 100 grad is 90 degrees
574                     fAngle *= F_PI / 200.0;
575                 }
576 
577                 return true;
578             }
579 
580             return false;
581         }
582 
583         sal_Int32 read_hex(const sal_Unicode& rChar)
584         {
585             if(rChar >= sal_Unicode('0') && rChar <=sal_Unicode('9'))
586             {
587                 return sal_Int32(rChar - sal_Unicode('0'));
588             }
589             else if(rChar >= sal_Unicode('A') && rChar <=sal_Unicode('F'))
590             {
591                 return 10 + sal_Int32(rChar - sal_Unicode('A'));
592             }
593             else if(rChar >= sal_Unicode('a') && rChar <=sal_Unicode('f'))
594             {
595                 return 10 + sal_Int32(rChar - sal_Unicode('a'));
596             }
597             else
598             {
599                 // error
600                 return 0;
601             }
602         }
603 
604         bool match_colorKeyword(basegfx::BColor& rColor, const rtl::OUString& rName)
605         {
606             typedef std::hash_map< rtl::OUString, Color, rtl::OUStringHash > ColorTokenMapper;
607             typedef std::pair< rtl::OUString, Color > ColorTokenValueType;
608             ColorTokenMapper aColorTokenMapperList;
609 
610             if(aColorTokenMapperList.empty())
611             {
612                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("aliceblue"), Color(240, 248, 255)));
613                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("antiquewhite"), Color(250, 235, 215)));
614                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("aqua"), Color( 0, 255, 255)));
615                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("aquamarine"), Color(127, 255, 212)));
616                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("azure"), Color(240, 255, 255)));
617                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("beige"), Color(245, 245, 220)));
618                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("bisque"), Color(255, 228, 196)));
619                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("black"), Color( 0, 0, 0)));
620                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("blanchedalmond"), Color(255, 235, 205)));
621                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("blue"), Color( 0, 0, 255)));
622                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("blueviolet"), Color(138, 43, 226)));
623                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("brown"), Color(165, 42, 42)));
624                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("burlywood"), Color(222, 184, 135)));
625                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("cadetblue"), Color( 95, 158, 160)));
626                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("chartreuse"), Color(127, 255, 0)));
627                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("chocolate"), Color(210, 105, 30)));
628                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("coral"), Color(255, 127, 80)));
629                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("cornflowerblue"), Color(100, 149, 237)));
630                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("cornsilk"), Color(255, 248, 220)));
631                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("crimson"), Color(220, 20, 60)));
632                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("cyan"), Color( 0, 255, 255)));
633                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkblue"), Color( 0, 0, 139)));
634                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkcyan"), Color( 0, 139, 139)));
635                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkgoldenrod"), Color(184, 134, 11)));
636                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkgray"), Color(169, 169, 169)));
637                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkgreen"), Color( 0, 100, 0)));
638                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkgrey"), Color(169, 169, 169)));
639                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkkhaki"), Color(189, 183, 107)));
640                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkmagenta"), Color(139, 0, 139)));
641                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkolivegreen"), Color( 85, 107, 47)));
642                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkorange"), Color(255, 140, 0)));
643                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkorchid"), Color(153, 50, 204)));
644                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkred"), Color(139, 0, 0)));
645                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darksalmon"), Color(233, 150, 122)));
646                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkseagreen"), Color(143, 188, 143)));
647                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkslateblue"), Color( 72, 61, 139)));
648                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkslategray"), Color( 47, 79, 79)));
649                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkslategrey"), Color( 47, 79, 79)));
650                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkturquoise"), Color( 0, 206, 209)));
651                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("darkviolet"), Color(148, 0, 211)));
652                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("deeppink"), Color(255, 20, 147)));
653                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("deepskyblue"), Color( 0, 191, 255)));
654                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("dimgray"), Color(105, 105, 105)));
655                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("dimgrey"), Color(105, 105, 105)));
656                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("dodgerblue"), Color( 30, 144, 255)));
657                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("firebrick"), Color(178, 34, 34)));
658                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("floralwhite"), Color(255, 250, 240)));
659                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("forestgreen"), Color( 34, 139, 34)));
660                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("fuchsia"), Color(255, 0, 255)));
661                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("gainsboro"), Color(220, 220, 220)));
662                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("ghostwhite"), Color(248, 248, 255)));
663                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("gold"), Color(255, 215, 0)));
664                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("goldenrod"), Color(218, 165, 32)));
665                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("gray"), Color(128, 128, 128)));
666                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("grey"), Color(128, 128, 128)));
667                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("green"), Color(0, 128, 0)));
668                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("greenyellow"), Color(173, 255, 47)));
669                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("honeydew"), Color(240, 255, 240)));
670                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("hotpink"), Color(255, 105, 180)));
671                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("indianred"), Color(205, 92, 92)));
672                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("indigo"), Color( 75, 0, 130)));
673                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("ivory"), Color(255, 255, 240)));
674                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("khaki"), Color(240, 230, 140)));
675                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lavender"), Color(230, 230, 250)));
676                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lavenderblush"), Color(255, 240, 245)));
677                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lawngreen"), Color(124, 252, 0)));
678                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lemonchiffon"), Color(255, 250, 205)));
679                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightblue"), Color(173, 216, 230)));
680                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightcoral"), Color(240, 128, 128)));
681                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightcyan"), Color(224, 255, 255)));
682                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightgoldenrodyellow"), Color(250, 250, 210)));
683                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightgray"), Color(211, 211, 211)));
684                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightgreen"), Color(144, 238, 144)));
685                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightgrey"), Color(211, 211, 211)));
686                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightpink"), Color(255, 182, 193)));
687                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightsalmon"), Color(255, 160, 122)));
688                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightseagreen"), Color( 32, 178, 170)));
689                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightskyblue"), Color(135, 206, 250)));
690                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightslategray"), Color(119, 136, 153)));
691                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightslategrey"), Color(119, 136, 153)));
692                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightsteelblue"), Color(176, 196, 222)));
693                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lightyellow"), Color(255, 255, 224)));
694                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("lime"), Color( 0, 255, 0)));
695                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("limegreen"), Color( 50, 205, 50)));
696                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("linen"), Color(250, 240, 230)));
697                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("magenta"), Color(255, 0, 255)));
698                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("maroon"), Color(128, 0, 0)));
699                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mediumaquamarine"), Color(102, 205, 170)));
700                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mediumblue"), Color( 0, 0, 205)));
701                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mediumorchid"), Color(186, 85, 211)));
702                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mediumpurple"), Color(147, 112, 219)));
703                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mediumseagreen"), Color( 60, 179, 113)));
704                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mediumslateblue"), Color(123, 104, 238)));
705                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mediumspringgreen"), Color( 0, 250, 154)));
706                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mediumturquoise"), Color( 72, 209, 204)));
707                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mediumvioletred"), Color(199, 21, 133)));
708                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("midnightblue"), Color( 25, 25, 112)));
709                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mintcream"), Color(245, 255, 250)));
710                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("mistyrose"), Color(255, 228, 225)));
711                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("moccasin"), Color(255, 228, 181)));
712                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("navajowhite"), Color(255, 222, 173)));
713                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("navy"), Color( 0, 0, 128)));
714                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("oldlace"), Color(253, 245, 230)));
715                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("olive"), Color(128, 128, 0)));
716                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("olivedrab"), Color(107, 142, 35)));
717                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("orange"), Color(255, 165, 0)));
718                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("orangered"), Color(255, 69, 0)));
719                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("orchid"), Color(218, 112, 214)));
720                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("palegoldenrod"), Color(238, 232, 170)));
721                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("palegreen"), Color(152, 251, 152)));
722                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("paleturquoise"), Color(175, 238, 238)));
723                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("palevioletred"), Color(219, 112, 147)));
724                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("papayawhip"), Color(255, 239, 213)));
725                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("peachpuff"), Color(255, 218, 185)));
726                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("peru"), Color(205, 133, 63)));
727                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("pink"), Color(255, 192, 203)));
728                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("plum"), Color(221, 160, 221)));
729                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("powderblue"), Color(176, 224, 230)));
730                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("purple"), Color(128, 0, 128)));
731                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("red"), Color(255, 0, 0)));
732                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("rosybrown"), Color(188, 143, 143)));
733                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("royalblue"), Color( 65, 105, 225)));
734                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("saddlebrown"), Color(139, 69, 19)));
735                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("salmon"), Color(250, 128, 114)));
736                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("sandybrown"), Color(244, 164, 96)));
737                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("seagreen"), Color( 46, 139, 87)));
738                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("seashell"), Color(255, 245, 238)));
739                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("sienna"), Color(160, 82, 45)));
740                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("silver"), Color(192, 192, 192)));
741                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("skyblue"), Color(135, 206, 235)));
742                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("slateblue"), Color(106, 90, 205)));
743                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("slategray"), Color(112, 128, 144)));
744                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("slategrey"), Color(112, 128, 144)));
745                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("snow"), Color(255, 250, 250)));
746                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("springgreen"), Color( 0, 255, 127)));
747                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("steelblue"), Color( 70, 130, 180)));
748                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("tan"), Color(210, 180, 140)));
749                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("teal"), Color( 0, 128, 128)));
750                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("thistle"), Color(216, 191, 216)));
751                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("tomato"), Color(255, 99, 71)));
752                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("turquoise"), Color( 64, 224, 208)));
753                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("violet"), Color(238, 130, 238)));
754                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("wheat"), Color(245, 222, 179)));
755                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("white"), Color(255, 255, 255)));
756                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("whitesmoke"), Color(245, 245, 245)));
757                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("yellow"), Color(255, 255, 0)));
758                 aColorTokenMapperList.insert(ColorTokenValueType(rtl::OUString::createFromAscii("yellowgreen"), Color(154, 205, 50)));
759             }
760 
761             const ColorTokenMapper::const_iterator aResult(aColorTokenMapperList.find(rName));
762 
763             if(aResult == aColorTokenMapperList.end())
764             {
765                 return false;
766             }
767             else
768             {
769                 rColor = aResult->second.getBColor();
770                 return true;
771             }
772         }
773 
774         bool read_color(const rtl::OUString& rCandidate, basegfx::BColor& rColor)
775         {
776             const sal_Int32 nLen(rCandidate.getLength());
777 
778             if(nLen)
779             {
780                 const sal_Unicode aChar(rCandidate[0]);
781                 const double fFactor(1.0 / 255.0);
782 
783                 if(aChar == sal_Unicode('#'))
784                 {
785                     // hex definition
786                     rtl::OUStringBuffer aNum;
787                     sal_Int32 nPos(1);
788 
789                     copyHex(rCandidate, nPos, aNum, nLen);
790                     const sal_Int32 nLength(aNum.getLength());
791 
792                     if(3 == nLength)
793                     {
794                         const sal_Int32 nR(read_hex(aNum.charAt(0)));
795                         const sal_Int32 nG(read_hex(aNum.charAt(1)));
796                         const sal_Int32 nB(read_hex(aNum.charAt(2)));
797 
798                         rColor.setRed((nR | (nR << 4)) * fFactor);
799                         rColor.setGreen((nG | (nG << 4)) * fFactor);
800                         rColor.setBlue((nB | (nB << 4)) * fFactor);
801 
802                         return true;
803                     }
804                     else if(6 == nLength)
805                     {
806                         const sal_Int32 nR1(read_hex(aNum.charAt(0)));
807                         const sal_Int32 nR2(read_hex(aNum.charAt(1)));
808                         const sal_Int32 nG1(read_hex(aNum.charAt(2)));
809                         const sal_Int32 nG2(read_hex(aNum.charAt(3)));
810                         const sal_Int32 nB1(read_hex(aNum.charAt(4)));
811                         const sal_Int32 nB2(read_hex(aNum.charAt(5)));
812 
813                         rColor.setRed((nR2 | (nR1 << 4)) * fFactor);
814                         rColor.setGreen((nG2 | (nG1 << 4)) * fFactor);
815                         rColor.setBlue((nB2 | (nB1 << 4)) * fFactor);
816 
817                         return true;
818                     }
819                 }
820                 else
821                 {
822                     static rtl::OUString aStrRgb(rtl::OUString::createFromAscii("rgb"));
823 
824                     if(rCandidate.matchIgnoreAsciiCase(aStrRgb, 0))
825                     {
826                         // rgb definition
827                         sal_Int32 nPos(aStrRgb.getLength());
828                         skip_char(rCandidate, sal_Unicode(' '), sal_Unicode('('), nPos, nLen);
829                         double fR(0.0);
830 
831                         if(readNumber(rCandidate, nPos, fR, nLen))
832                         {
833                             skip_char(rCandidate, sal_Unicode(' '), nPos, nLen);
834 
835                             if(nPos < nLen)
836                             {
837                                 const sal_Unicode aChar(rCandidate[nPos]);
838                                 const bool bIsPercent(sal_Unicode('%') == aChar);
839                                 double fG(0.0);
840 
841                                 if(bIsPercent)
842                                 {
843                                     skip_char(rCandidate, sal_Unicode('%'), nPos, nLen);
844                                 }
845 
846                                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
847 
848                                 if(readNumber(rCandidate, nPos, fG, nLen))
849                                 {
850                                     double fB(0.0);
851 
852                                     if(bIsPercent)
853                                     {
854                                         skip_char(rCandidate, sal_Unicode('%'), nPos, nLen);
855                                     }
856 
857                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
858 
859                                     if(readNumber(rCandidate, nPos, fB, nLen))
860                                     {
861                                         const double fFac(bIsPercent ? 0.01 : fFactor);
862 
863                                         rColor.setRed(fR * fFac);
864                                         rColor.setGreen(fG * fFac);
865                                         rColor.setBlue(fB * fFac);
866 
867                                         if(bIsPercent)
868                                         {
869                                             skip_char(rCandidate, sal_Unicode('%'), nPos, nLen);
870                                         }
871 
872                                         skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(')'), nPos, nLen);
873                                         return true;
874                                     }
875                                 }
876                             }
877                         }
878                     }
879                     else
880                     {
881                         // color keyword
882                         if(match_colorKeyword(rColor, rCandidate))
883                         {
884                             return true;
885                         }
886                     }
887                 }
888             }
889 
890             return false;
891         }
892 
893         basegfx::B2DRange readViewBox(const rtl::OUString& rCandidate, InfoProvider& rInfoProvider)
894         {
895             const sal_Int32 nLen(rCandidate.getLength());
896 
897             if(nLen)
898             {
899                 sal_Int32 nPos(0);
900                 SvgNumber aMinX;
901                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
902 
903                 if(readNumberAndUnit(rCandidate, nPos, aMinX, nLen))
904                 {
905                     SvgNumber aMinY;
906                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
907 
908                     if(readNumberAndUnit(rCandidate, nPos, aMinY, nLen))
909                     {
910                         SvgNumber aWidth;
911                         skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
912 
913                         if(readNumberAndUnit(rCandidate, nPos, aWidth, nLen))
914                         {
915                             SvgNumber aHeight;
916                             skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
917 
918                             if(readNumberAndUnit(rCandidate, nPos, aHeight, nLen))
919                             {
920                                 return basegfx::B2DRange(
921                                     aMinX.solve(rInfoProvider, xcoordinate),
922                                     aMinY.solve(rInfoProvider, ycoordinate),
923                                     aWidth.solve(rInfoProvider, xcoordinate),
924                                     aHeight.solve(rInfoProvider, ycoordinate));
925                             }
926                         }
927                     }
928                 }
929             }
930 
931             return basegfx::B2DRange();
932         }
933 
934         basegfx::B2DHomMatrix readTransform(const rtl::OUString& rCandidate, InfoProvider& rInfoProvider)
935         {
936             basegfx::B2DHomMatrix aMatrix;
937             const sal_Int32 nLen(rCandidate.getLength());
938 
939             if(nLen)
940             {
941                 sal_Int32 nPos(0);
942                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
943 
944                 while(nPos < nLen)
945                 {
946                     const sal_Unicode aChar(rCandidate[nPos]);
947                     const sal_Int32 nInitPos(nPos);
948                     static rtl::OUString aStrMatrix(rtl::OUString::createFromAscii("matrix"));
949                     static rtl::OUString aStrTranslate(rtl::OUString::createFromAscii("translate"));
950                     static rtl::OUString aStrScale(rtl::OUString::createFromAscii("scale"));
951                     static rtl::OUString aStrRotate(rtl::OUString::createFromAscii("rotate"));
952                     static rtl::OUString aStrSkewX(rtl::OUString::createFromAscii("skewX"));
953                     static rtl::OUString aStrSkewY(rtl::OUString::createFromAscii("skewY"));
954 
955                     switch(aChar)
956                     {
957                         case sal_Unicode('m') :
958                         {
959                             if(rCandidate.match(aStrMatrix, nPos))
960                             {
961                                 // matrix element
962                                 nPos += aStrMatrix.getLength();
963                                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode('('), nPos, nLen);
964                                 SvgNumber aVal;
965                                 basegfx::B2DHomMatrix aNew;
966 
967                                 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
968                                 {
969                                     aNew.set(0, 0, aVal.solve(rInfoProvider)); // Element A
970                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
971 
972                                     if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
973                                     {
974                                         aNew.set(1, 0, aVal.solve(rInfoProvider)); // Element B
975                                         skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
976 
977                                         if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
978                                         {
979                                             aNew.set(0, 1, aVal.solve(rInfoProvider)); // Element C
980                                             skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
981 
982                                             if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
983                                             {
984                                                 aNew.set(1, 1, aVal.solve(rInfoProvider)); // Element D
985                                                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
986 
987                                                 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
988                                                 {
989                                                     aNew.set(0, 2, aVal.solve(rInfoProvider, xcoordinate)); // Element E
990                                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
991 
992                                                     if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
993                                                     {
994                                                         aNew.set(1, 2, aVal.solve(rInfoProvider, ycoordinate)); // Element F
995                                                         skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(')'), nPos, nLen);
996                                                         skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
997 
998                                                         // caution: String is evaluated from left to right, but matrix multiplication
999                                                         // in SVG is right to left, so put the new transformation before the current
1000                                                         // one by multiplicating from the right side
1001                                                         aMatrix = aMatrix * aNew;
1002                                                     }
1003                                                 }
1004                                             }
1005                                         }
1006                                     }
1007                                 }
1008                             }
1009                             break;
1010                         }
1011                         case sal_Unicode('t') :
1012                         {
1013                             if(rCandidate.match(aStrTranslate, nPos))
1014                             {
1015                                 // translate element
1016                                 nPos += aStrTranslate.getLength();
1017                                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode('('), nPos, nLen);
1018                                 SvgNumber aTransX;
1019 
1020                                 if(readNumberAndUnit(rCandidate, nPos, aTransX, nLen))
1021                                 {
1022                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1023                                     SvgNumber aTransY;
1024                                     readNumberAndUnit(rCandidate, nPos, aTransY, nLen);
1025                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(')'), nPos, nLen);
1026                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1027 
1028                                     aMatrix = aMatrix * basegfx::tools::createTranslateB2DHomMatrix(
1029                                         aTransX.solve(rInfoProvider, xcoordinate),
1030                                         aTransY.solve(rInfoProvider, ycoordinate));
1031                                 }
1032                             }
1033                             break;
1034                         }
1035                         case sal_Unicode('s') :
1036                         {
1037                             if(rCandidate.match(aStrScale, nPos))
1038                             {
1039                                 // scale element
1040                                 nPos += aStrScale.getLength();
1041                                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode('('), nPos, nLen);
1042                                 SvgNumber aScaleX;
1043 
1044                                 if(readNumberAndUnit(rCandidate, nPos, aScaleX, nLen))
1045                                 {
1046                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1047                                     SvgNumber aScaleY(aScaleX);
1048                                     readNumberAndUnit(rCandidate, nPos, aScaleY, nLen);
1049                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(')'), nPos, nLen);
1050                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1051 
1052                                     aMatrix = aMatrix * basegfx::tools::createScaleB2DHomMatrix(
1053                                         aScaleX.solve(rInfoProvider),
1054                                         aScaleY.solve(rInfoProvider));
1055                                 }
1056                             }
1057                             else if(rCandidate.match(aStrSkewX, nPos))
1058                             {
1059                                 // skewx element
1060                                 nPos += aStrSkewX.getLength();
1061                                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode('('), nPos, nLen);
1062                                 double fSkewX(0.0);
1063 
1064                                 if(readAngle(rCandidate, nPos, fSkewX, nLen))
1065                                 {
1066                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(')'), nPos, nLen);
1067                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1068 
1069                                     aMatrix = aMatrix * basegfx::tools::createShearXB2DHomMatrix(tan(fSkewX));
1070                                 }
1071                             }
1072                             else if(rCandidate.match(aStrSkewY, nPos))
1073                             {
1074                                 // skewy element
1075                                 nPos += aStrSkewY.getLength();
1076                                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode('('), nPos, nLen);
1077                                 double fSkewY(0.0);
1078 
1079                                 if(readAngle(rCandidate, nPos, fSkewY, nLen))
1080                                 {
1081                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(')'), nPos, nLen);
1082                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1083 
1084                                     aMatrix = aMatrix * basegfx::tools::createShearYB2DHomMatrix(tan(fSkewY));
1085                                 }
1086                             }
1087                             break;
1088                         }
1089                         case sal_Unicode('r') :
1090                         {
1091                             if(rCandidate.match(aStrRotate, nPos))
1092                             {
1093                                 // rotate element
1094                                 nPos += aStrRotate.getLength();
1095                                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode('('), nPos, nLen);
1096                                 double fAngle(0.0);
1097 
1098                                 if(readAngle(rCandidate, nPos, fAngle, nLen))
1099                                 {
1100                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1101                                     SvgNumber aX;
1102                                     readNumberAndUnit(rCandidate, nPos, aX, nLen);
1103                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1104                                     SvgNumber aY;
1105                                     readNumberAndUnit(rCandidate, nPos, aY, nLen);
1106                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(')'), nPos, nLen);
1107                                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1108 
1109                                     const double fX(aX.isSet() ? aX.solve(rInfoProvider, xcoordinate) : 0.0);
1110                                     const double fY(aY.isSet() ? aY.solve(rInfoProvider, ycoordinate) : 0.0);
1111 
1112                                     if(!basegfx::fTools::equalZero(fX) || !basegfx::fTools::equalZero(fY))
1113                                     {
1114                                         // rotate around point
1115                                         aMatrix = aMatrix * basegfx::tools::createRotateAroundPoint(fX, fY, fAngle);
1116                                     }
1117                                     else
1118                                     {
1119                                         // rotate
1120                                         aMatrix = aMatrix * basegfx::tools::createRotateB2DHomMatrix(fAngle);
1121                                     }
1122                                 }
1123                             }
1124                             break;
1125                         }
1126                     }
1127 
1128                     if(nInitPos == nPos)
1129                     {
1130                         OSL_ENSURE(false, "Could not interpret on current position (!)");
1131                         nPos++;
1132                     }
1133                 }
1134             }
1135 
1136             return aMatrix;
1137         }
1138 
1139         bool readSingleNumber(const rtl::OUString& rCandidate, SvgNumber& aNum)
1140         {
1141             const sal_Int32 nLen(rCandidate.getLength());
1142             sal_Int32 nPos(0);
1143 
1144             return readNumberAndUnit(rCandidate, nPos, aNum, nLen);
1145         }
1146 
1147         bool readLocalUrl(const rtl::OUString& rCandidate, rtl::OUString& rURL)
1148         {
1149             static rtl::OUString aStrUrl(rtl::OUString::createFromAscii("url"));
1150 
1151             if(rCandidate.match(aStrUrl, 0))
1152             {
1153                 const sal_Int32 nLen(rCandidate.getLength());
1154                 sal_Int32 nPos(aStrUrl.getLength());
1155 
1156                 skip_char(rCandidate, sal_Unicode('('), sal_Unicode('#'), nPos, nLen);
1157                 rtl::OUStringBuffer aTokenValue;
1158                 copyToLimiter(rCandidate, sal_Unicode(')'), nPos, aTokenValue, nLen);
1159                 rURL = aTokenValue.makeStringAndClear();
1160 
1161                 return true;
1162             }
1163 
1164             return false;
1165         }
1166 
1167         bool readSvgPaint(const rtl::OUString& rCandidate, SvgPaint& rSvgPaint, rtl::OUString& rURL)
1168         {
1169             const sal_Int32 nLen(rCandidate.getLength());
1170 
1171             if(nLen)
1172             {
1173                 basegfx::BColor aColor;
1174 
1175                 if(read_color(rCandidate, aColor))
1176                 {
1177                     rSvgPaint = SvgPaint(aColor, true, true);
1178                     return true;
1179                 }
1180                 else
1181                 {
1182                     static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none"));
1183                     static rtl::OUString aStrCurrentColor(rtl::OUString::createFromAscii("currentColor"));
1184 
1185                     if(rCandidate.match(aStrNone, 0))
1186                     {
1187                         rSvgPaint = SvgPaint(aColor, true, false, false);
1188                         return true;
1189                     }
1190                     else if(readLocalUrl(rCandidate, rURL))
1191                     {
1192                         /// Url is copied to rURL, but needs to be solved outside this helper
1193                         return false;
1194                     }
1195                     else if(rCandidate.match(aStrCurrentColor, 0))
1196                     {
1197                         rSvgPaint = SvgPaint(aColor, true, true, true);
1198                         return true;
1199                     }
1200                 }
1201             }
1202 
1203             return false;
1204         }
1205 
1206         bool readSvgNumberVector(const rtl::OUString& rCandidate, SvgNumberVector& rSvgNumberVector)
1207         {
1208             const sal_Int32 nLen(rCandidate.getLength());
1209             rSvgNumberVector.clear();
1210 
1211             if(nLen)
1212             {
1213                 sal_Int32 nPos(0);
1214                 SvgNumber aNum;
1215                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1216 
1217                 while(readNumberAndUnit(rCandidate, nPos, aNum, nLen))
1218                 {
1219                     rSvgNumberVector.push_back(aNum);
1220                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1221                 }
1222 
1223                 return !rSvgNumberVector.empty();
1224             }
1225 
1226             return false;
1227         }
1228 
1229         SvgAspectRatio readSvgAspectRatio(const rtl::OUString& rCandidate)
1230         {
1231             const sal_Int32 nLen(rCandidate.getLength());
1232 
1233             if(nLen)
1234             {
1235                 sal_Int32 nPos(0);
1236                 SvgAlign aSvgAlign(Align_xMidYMid);
1237                 bool bDefer(false);
1238                 bool bMeetOrSlice(true);
1239                 bool bChanged(false);
1240 
1241                 while(nPos < nLen)
1242                 {
1243                     const sal_Int32 nInitPos(nPos);
1244                     skip_char(rCandidate, sal_Unicode(' '), nPos, nLen);
1245                     rtl::OUStringBuffer aTokenName;
1246                     copyString(rCandidate, nPos, aTokenName, nLen);
1247 
1248                     if(aTokenName.getLength())
1249                     {
1250                         switch(StrToSVGToken(aTokenName.makeStringAndClear()))
1251                         {
1252                             case SVGTokenDefer:
1253                             {
1254                                 bDefer = true;
1255                                 bChanged = true;
1256                                 break;
1257                             }
1258                             case SVGTokenNone:
1259                             {
1260                                 aSvgAlign = Align_none;
1261                                 bChanged = true;
1262                                 break;
1263                             }
1264                             case SVGTokenXMinYMin:
1265                             {
1266                                 aSvgAlign = Align_xMinYMin;
1267                                 bChanged = true;
1268                                 break;
1269                             }
1270                             case SVGTokenXMidYMin:
1271                             {
1272                                 aSvgAlign = Align_xMidYMin;
1273                                 bChanged = true;
1274                                 break;
1275                             }
1276                             case SVGTokenXMaxYMin:
1277                             {
1278                                 aSvgAlign = Align_xMaxYMin;
1279                                 bChanged = true;
1280                                 break;
1281                             }
1282                             case SVGTokenXMinYMid:
1283                             {
1284                                 aSvgAlign = Align_xMinYMid;
1285                                 bChanged = true;
1286                                 break;
1287                             }
1288                             case SVGTokenXMidYMid:
1289                             {
1290                                 aSvgAlign = Align_xMidYMid;
1291                                 bChanged = true;
1292                                 break;
1293                             }
1294                             case SVGTokenXMaxYMid:
1295                             {
1296                                 aSvgAlign = Align_xMaxYMid;
1297                                 bChanged = true;
1298                                 break;
1299                             }
1300                             case SVGTokenXMinYMax:
1301                             {
1302                                 aSvgAlign = Align_xMinYMax;
1303                                 bChanged = true;
1304                                 break;
1305                             }
1306                             case SVGTokenXMidYMax:
1307                             {
1308                                 aSvgAlign = Align_xMidYMax;
1309                                 bChanged = true;
1310                                 break;
1311                             }
1312                             case SVGTokenXMaxYMax:
1313                             {
1314                                 aSvgAlign = Align_xMaxYMax;
1315                                 bChanged = true;
1316                                 break;
1317                             }
1318                             case SVGTokenMeet:
1319                             {
1320                                 bMeetOrSlice = true;
1321                                 bChanged = true;
1322                                 break;
1323                             }
1324                             case SVGTokenSlice:
1325                             {
1326                                 bMeetOrSlice = false;
1327                                 bChanged = true;
1328                                 break;
1329                             }
1330                         }
1331                     }
1332 
1333                     if(nInitPos == nPos)
1334                     {
1335                         OSL_ENSURE(false, "Could not interpret on current position (!)");
1336                         nPos++;
1337                     }
1338                 }
1339 
1340                 if(bChanged)
1341                 {
1342                     return SvgAspectRatio(aSvgAlign, bDefer, bMeetOrSlice);
1343                 }
1344             }
1345 
1346             return SvgAspectRatio();
1347         }
1348 
1349         bool readSvgStringVector(const rtl::OUString& rCandidate, SvgStringVector& rSvgStringVector)
1350         {
1351             rSvgStringVector.clear();
1352             const sal_Int32 nLen(rCandidate.getLength());
1353 
1354             if(nLen)
1355             {
1356                 sal_Int32 nPos(0);
1357                 rtl::OUStringBuffer aTokenValue;
1358                 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1359 
1360                 while(nPos < nLen)
1361                 {
1362                     copyToLimiter(rCandidate, sal_Unicode(','), nPos, aTokenValue, nLen);
1363                     skip_char(rCandidate, sal_Unicode(','), sal_Unicode(' '), nPos, nLen);
1364                     const rtl::OUString aString = aTokenValue.makeStringAndClear();
1365 
1366                     if(aString.getLength())
1367                     {
1368                         rSvgStringVector.push_back(aString);
1369                     }
1370                 }
1371             }
1372 
1373             return !rSvgStringVector.empty();
1374         }
1375 
1376         void readImageLink(const rtl::OUString& rCandidate, rtl::OUString& rXLink, rtl::OUString& rUrl, rtl::OUString& rMimeType, rtl::OUString& rData)
1377         {
1378             rXLink = rUrl = rMimeType = rData = rtl::OUString();
1379 
1380             if(sal_Unicode('#') == rCandidate[0])
1381             {
1382                 // local link
1383                 rXLink = rCandidate.copy(1);
1384             }
1385             else
1386             {
1387                 static rtl::OUString aStrData(rtl::OUString::createFromAscii("data:"));
1388 
1389                 if(rCandidate.match(aStrData, 0))
1390                 {
1391                     // embedded data
1392                     sal_Int32 nPos(aStrData.getLength());
1393                     sal_Int32 nLen(rCandidate.getLength());
1394                     rtl::OUStringBuffer aBuffer;
1395 
1396                     // read mime type
1397                     skip_char(rCandidate, sal_Unicode(' '), nPos, nLen);
1398                     copyToLimiter(rCandidate, sal_Unicode(';'), nPos, aBuffer, nLen);
1399                     skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(';'), nPos, nLen);
1400                     rMimeType = aBuffer.makeStringAndClear();
1401 
1402                     if(rMimeType.getLength() && nPos < nLen)
1403                     {
1404                         static rtl::OUString aStrImage(rtl::OUString::createFromAscii("image"));
1405 
1406                         if(rMimeType.match(aStrImage, 0))
1407                         {
1408                             // image data
1409                             rtl::OUString aData(rCandidate.copy(nPos));
1410                             static rtl::OUString aStrBase64(rtl::OUString::createFromAscii("base64"));
1411 
1412                             if(aData.match(aStrBase64, 0))
1413                             {
1414                                 // base64 encoded
1415                                 nPos = aStrBase64.getLength();
1416                                 nLen = aData.getLength();
1417 
1418                                 skip_char(aData, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
1419 
1420                                 if(nPos < nLen)
1421                                 {
1422                                     rData = aData.copy(nPos);
1423                                 }
1424                             }
1425                         }
1426                     }
1427                 }
1428                 else
1429                 {
1430                     // Url (path and filename)
1431                     rUrl = rCandidate;
1432                 }
1433             }
1434         }
1435 
1436         rtl::OUString convert(const rtl::OUString& rCandidate, const sal_Unicode& rPattern, const sal_Unicode& rNew, bool bRemove)
1437         {
1438             const sal_Int32 nLen(rCandidate.getLength());
1439 
1440             if(nLen)
1441             {
1442                 sal_Int32 nPos(0);
1443                 rtl::OUStringBuffer aBuffer;
1444                 bool bChanged(false);
1445 
1446                 while(nPos < nLen)
1447                 {
1448                     const sal_Unicode aChar(rCandidate[nPos]);
1449 
1450                     if(rPattern == aChar)
1451                     {
1452                         bChanged = true;
1453 
1454                         if(!bRemove)
1455                         {
1456                             aBuffer.append(rNew);
1457                         }
1458                     }
1459                     else
1460                     {
1461                         aBuffer.append(aChar);
1462                     }
1463 
1464                     nPos++;
1465                 }
1466 
1467                 if(bChanged)
1468                 {
1469                     return aBuffer.makeStringAndClear();
1470                 }
1471             }
1472 
1473             return rCandidate;
1474         }
1475 
1476         rtl::OUString consolidateContiguosSpace(const rtl::OUString& rCandidate)
1477         {
1478             const sal_Int32 nLen(rCandidate.getLength());
1479 
1480             if(nLen)
1481             {
1482                 sal_Int32 nPos(0);
1483                 rtl::OUStringBuffer aBuffer;
1484                 bool bInsideSpace(false);
1485                 const sal_Unicode aSpace(' ');
1486 
1487                 while(nPos < nLen)
1488                 {
1489                     const sal_Unicode aChar(rCandidate[nPos]);
1490 
1491                     if(aSpace == aChar)
1492                     {
1493                         bInsideSpace = true;
1494                     }
1495                     else
1496                     {
1497                         if(bInsideSpace)
1498                         {
1499                             bInsideSpace = false;
1500                             aBuffer.append(aSpace);
1501                         }
1502 
1503                         aBuffer.append(aChar);
1504                     }
1505 
1506                     nPos++;
1507                 }
1508 
1509                 if(bInsideSpace)
1510                 {
1511                     aBuffer.append(aSpace);
1512                 }
1513 
1514                 if(aBuffer.getLength() != nLen)
1515                 {
1516                     return aBuffer.makeStringAndClear();
1517                 }
1518             }
1519 
1520             return rCandidate;
1521         }
1522 
1523         rtl::OUString whiteSpaceHandlingDefault(const rtl::OUString& rCandidate)
1524         {
1525             const sal_Unicode aNewline('\n');
1526             const sal_Unicode aTab('\t');
1527             const sal_Unicode aSpace(' ');
1528 
1529             // remove all newline characters
1530             rtl::OUString aRetval(convert(rCandidate, aNewline, aNewline, true));
1531 
1532             // convert tab to space
1533             aRetval = convert(aRetval, aTab, aSpace, false);
1534 
1535             // strip of all leading and trailing spaces
1536             aRetval = aRetval.trim();
1537 
1538             // consolidate contiguos space
1539             aRetval = consolidateContiguosSpace(aRetval);
1540 
1541             return aRetval;
1542         }
1543 
1544         rtl::OUString whiteSpaceHandlingPreserve(const rtl::OUString& rCandidate)
1545         {
1546             const sal_Unicode aNewline('\n');
1547             const sal_Unicode aTab('\t');
1548             const sal_Unicode aSpace(' ');
1549 
1550             // convert newline to space
1551             rtl::OUString aRetval(convert(rCandidate, aNewline, aSpace, false));
1552 
1553             // convert tab to space
1554             aRetval = convert(rCandidate, aTab, aSpace, false);
1555 
1556             return rCandidate;
1557         }
1558 
1559         ::std::vector< double > solveSvgNumberVector(const SvgNumberVector& rInput, const InfoProvider& rInfoProvider, NumberType aNumberType)
1560         {
1561             ::std::vector< double > aRetval;
1562 
1563             if(!rInput.empty())
1564             {
1565                 const double nCount(rInput.size());
1566                 aRetval.reserve(nCount);
1567 
1568                 for(sal_uInt32 a(0); a < nCount; a++)
1569                 {
1570                     aRetval.push_back(rInput[a].solve(rInfoProvider, aNumberType));
1571                 }
1572             }
1573 
1574             return aRetval;
1575         }
1576 
1577     } // end of namespace svgreader
1578 } // end of namespace svgio
1579 
1580 //////////////////////////////////////////////////////////////////////////////
1581 // eof
1582