Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / svgio / source / svgreader / svgtools.cxx
blob79db5ebedc381458b9f62b533c4ceb7b9c4aa662
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svgtools.hxx>
21 #include <osl/thread.h>
22 #include <tools/color.hxx>
23 #include <basegfx/matrix/b2dhommatrix.hxx>
24 #include <basegfx/matrix/b2dhommatrixtools.hxx>
25 #include <svgtoken.hxx>
26 #include <unordered_map>
28 namespace svgio
30 namespace svgreader
32 // common non-token strings
33 const OUString commonStrings::aStrUserSpaceOnUse("userSpaceOnUse");
34 const OUString commonStrings::aStrObjectBoundingBox("objectBoundingBox");
35 const OUString commonStrings::aStrNonzero("nonzero");
36 const OUString commonStrings::aStrEvenOdd("evenodd");
38 basegfx::B2DHomMatrix SvgAspectRatio::createLinearMapping(const basegfx::B2DRange& rTarget, const basegfx::B2DRange& rSource)
40 basegfx::B2DHomMatrix aRetval;
41 const double fSWidth(rSource.getWidth());
42 const double fSHeight(rSource.getHeight());
43 const bool bNoSWidth(basegfx::fTools::equalZero(fSWidth));
44 const bool bNoSHeight(basegfx::fTools::equalZero(fSHeight));
46 // transform from source state to unit range
47 aRetval.translate(-rSource.getMinX(), -rSource.getMinY());
48 aRetval.scale(
49 (bNoSWidth ? 1.0 : 1.0 / fSWidth) * rTarget.getWidth(),
50 (bNoSHeight ? 1.0 : 1.0 / fSHeight) * rTarget.getHeight());
52 // transform from unit rage to target range
53 aRetval.translate(rTarget.getMinX(), rTarget.getMinY());
55 return aRetval;
58 basegfx::B2DHomMatrix SvgAspectRatio::createMapping(const basegfx::B2DRange& rTarget, const basegfx::B2DRange& rSource) const
60 // removed !isSet() from below. Due to correct defaults in the constructor an instance
61 // of this class is perfectly useful without being set by any importer
62 if(Align_none == getSvgAlign())
64 // create linear mapping (default)
65 return createLinearMapping(rTarget, rSource);
68 basegfx::B2DHomMatrix aRetval;
70 const double fSWidth(rSource.getWidth());
71 const double fSHeight(rSource.getHeight());
72 const bool bNoSWidth(basegfx::fTools::equalZero(fSWidth));
73 const bool bNoSHeight(basegfx::fTools::equalZero(fSHeight));
74 const double fScaleX((bNoSWidth ? 1.0 : 1.0 / fSWidth) * rTarget.getWidth());
75 const double fScaleY((bNoSHeight ? 1.0 : 1.0 / fSHeight) * rTarget.getHeight());
76 const double fScale(isMeetOrSlice() ? std::min(fScaleX, fScaleY) : std::max(fScaleX, fScaleY));
78 // remove source translation, apply scale
79 aRetval.translate(-rSource.getMinX(), -rSource.getMinY());
80 aRetval.scale(fScale, fScale);
82 // evaluate horizontal alignment
83 const double fNewWidth(fSWidth * fScale);
84 double fTransX(0.0);
86 switch(getSvgAlign())
88 case Align_xMidYMin:
89 case Align_xMidYMid:
90 case Align_xMidYMax:
92 // centerX
93 const double fFreeSpace(rTarget.getWidth() - fNewWidth);
94 fTransX = fFreeSpace * 0.5;
95 break;
97 case Align_xMaxYMin:
98 case Align_xMaxYMid:
99 case Align_xMaxYMax:
101 // Right align
102 const double fFreeSpace(rTarget.getWidth() - fNewWidth);
103 fTransX = fFreeSpace;
104 break;
106 default: break;
109 // evaluate vertical alignment
110 const double fNewHeight(fSHeight * fScale);
111 double fTransY(0.0);
113 switch(getSvgAlign())
115 case Align_xMinYMid:
116 case Align_xMidYMid:
117 case Align_xMaxYMid:
119 // centerY
120 const double fFreeSpace(rTarget.getHeight() - fNewHeight);
121 fTransY = fFreeSpace * 0.5;
122 break;
124 case Align_xMinYMax:
125 case Align_xMidYMax:
126 case Align_xMaxYMax:
128 // Bottom align
129 const double fFreeSpace(rTarget.getHeight() - fNewHeight);
130 fTransY = fFreeSpace;
131 break;
133 default: break;
136 // add target translation
137 aRetval.translate(
138 rTarget.getMinX() + fTransX,
139 rTarget.getMinY() + fTransY);
141 return aRetval;
144 double SvgNumber::solveNonPercentage(const InfoProvider& rInfoProvider) const
146 if(isSet())
148 switch(meUnit)
150 case Unit_em:
152 return mfNumber * rInfoProvider.getCurrentFontSizeInherited();
154 case Unit_ex:
156 return mfNumber * rInfoProvider.getCurrentXHeightInherited() * 0.5;
158 case Unit_px:
160 return mfNumber;
162 case Unit_pt:
163 case Unit_pc:
164 case Unit_cm:
165 case Unit_mm:
166 case Unit_in:
168 double fRetval(mfNumber);
170 switch(meUnit)
172 case Unit_pt: fRetval *= F_SVG_PIXEL_PER_INCH / 72.0; break;
173 case Unit_pc: fRetval *= F_SVG_PIXEL_PER_INCH / 6.0; break;
174 case Unit_cm: fRetval *= F_SVG_PIXEL_PER_INCH / 2.54; break;
175 case Unit_mm: fRetval *= 0.1 * F_SVG_PIXEL_PER_INCH / 2.54; break;
176 case Unit_in: fRetval *= F_SVG_PIXEL_PER_INCH; break;
177 default: break;
180 return fRetval;
182 case Unit_none:
184 SAL_WARN("svgio", "Design error, this case should have been handled in the caller");
185 return mfNumber;
187 default:
189 OSL_ENSURE(false, "Do not use with percentage! ");
190 return 0.0;
195 /// not set
196 OSL_ENSURE(false, "SvgNumber not set (!)");
197 return 0.0;
200 double SvgNumber::solve(const InfoProvider& rInfoProvider, NumberType aNumberType) const
202 if(isSet())
204 switch(meUnit)
206 case Unit_px:
208 return mfNumber;
210 case Unit_pt:
211 case Unit_pc:
212 case Unit_cm:
213 case Unit_mm:
214 case Unit_in:
215 case Unit_em:
216 case Unit_ex:
217 case Unit_none:
219 return solveNonPercentage( rInfoProvider);
221 case Unit_percent:
223 double fRetval(mfNumber * 0.01);
224 basegfx::B2DRange aViewPort = rInfoProvider.getCurrentViewPort();
226 if ( aViewPort.isEmpty() )
228 SAL_WARN("svgio", "Design error, this case should have been handled in the caller");
229 // no viewPort, assume a normal page size (A4)
230 aViewPort = basegfx::B2DRange(
231 0.0,
232 0.0,
233 210.0 * F_SVG_PIXEL_PER_INCH / 2.54,
234 297.0 * F_SVG_PIXEL_PER_INCH / 2.54);
238 if ( !aViewPort.isEmpty() )
240 if(xcoordinate == aNumberType)
242 // it's a x-coordinate, relative to current width (w)
243 fRetval *= aViewPort.getWidth();
245 else if(ycoordinate == aNumberType)
247 // it's a y-coordinate, relative to current height (h)
248 fRetval *= aViewPort.getHeight();
250 else // length
252 // it's a length, relative to sqrt(w*w + h*h)/sqrt(2)
253 const double fCurrentWidth(aViewPort.getWidth());
254 const double fCurrentHeight(aViewPort.getHeight());
255 const double fCurrentLength(
256 sqrt(fCurrentWidth * fCurrentWidth + fCurrentHeight * fCurrentHeight)/sqrt(2.0));
258 fRetval *= fCurrentLength;
262 return fRetval;
264 default:
266 break;
271 /// not set
272 OSL_ENSURE(false, "SvgNumber not set (!)");
273 return 0.0;
276 bool SvgNumber::isPositive() const
278 return basegfx::fTools::moreOrEqual(mfNumber, 0.0);
281 void skip_char(const OUString& rCandidate, sal_Unicode nChar, sal_Int32& nPos, const sal_Int32 nLen)
283 while(nPos < nLen && nChar == rCandidate[nPos])
285 nPos++;
289 void skip_char(const OUString& rCandidate, sal_Unicode nCharA, sal_Unicode nCharB, sal_Int32& nPos, const sal_Int32 nLen)
291 while(nPos < nLen && (nCharA == rCandidate[nPos] || nCharB == rCandidate[nPos]))
293 nPos++;
297 void copySign(const OUString& rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
299 if(nPos < nLen)
301 const sal_Unicode aChar(rCandidate[nPos]);
303 if('+' == aChar || '-' == aChar)
305 rTarget.append(aChar);
306 nPos++;
311 void copyNumber(const OUString& rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
313 bool bOnNumber(true);
315 while(bOnNumber && nPos < nLen)
317 const sal_Unicode aChar(rCandidate[nPos]);
319 bOnNumber = ('0' <= aChar && '9' >= aChar) || '.' == aChar;
321 if(bOnNumber)
323 rTarget.append(aChar);
324 nPos++;
329 void copyHex(const OUString& rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
331 bool bOnHex(true);
333 while(bOnHex && nPos < nLen)
335 const sal_Unicode aChar(rCandidate[nPos]);
337 bOnHex = ('0' <= aChar && '9' >= aChar)
338 || ('A' <= aChar && 'F' >= aChar)
339 || ('a' <= aChar && 'f' >= aChar);
341 if(bOnHex)
343 rTarget.append(aChar);
344 nPos++;
349 void copyString(const OUString& rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
351 bool bOnChar(true);
353 while(bOnChar && nPos < nLen)
355 const sal_Unicode aChar(rCandidate[nPos]);
357 bOnChar = ('a' <= aChar && 'z' >= aChar)
358 || ('A' <= aChar && 'Z' >= aChar)
359 || '-' == aChar;
361 if(bOnChar)
363 rTarget.append(aChar);
364 nPos++;
369 void copyToLimiter(const OUString& rCandidate, sal_Unicode nLimiter, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
371 while(nPos < nLen && nLimiter != rCandidate[nPos])
373 rTarget.append(rCandidate[nPos]);
374 nPos++;
378 bool readNumber(const OUString& rCandidate, sal_Int32& nPos, double& fNum, const sal_Int32 nLen)
380 if(nPos < nLen)
382 OUStringBuffer aNum;
384 copySign(rCandidate, nPos, aNum, nLen);
385 copyNumber(rCandidate, nPos, aNum, nLen);
387 if(nPos < nLen)
389 const sal_Unicode aChar(rCandidate[nPos]);
391 if('e' == aChar || 'E' == aChar)
393 // try to read exponential number, but be careful. I had
394 // a case where dx="2em" was used, thus the 'e' was consumed
395 // by error. First try if there are numbers after the 'e',
396 // safe current state
397 nPos++;
398 const OUStringBuffer aNum2(aNum);
399 const sal_Int32 nPosAfterE(nPos);
401 aNum.append(aChar);
402 copySign(rCandidate, nPos, aNum, nLen);
403 copyNumber(rCandidate, nPos, aNum, nLen);
405 if(nPosAfterE == nPos)
407 // no number after 'e', go back. Do not
408 // return false, it's still a valid integer number
409 aNum = aNum2;
410 nPos--;
415 if(!aNum.isEmpty())
417 rtl_math_ConversionStatus eStatus;
419 fNum = rtl::math::stringToDouble(
420 aNum.makeStringAndClear(), '.', ',',
421 &eStatus);
423 return eStatus == rtl_math_ConversionStatus_Ok;
427 return false;
430 SvgUnit readUnit(const OUString& rCandidate, sal_Int32& nPos, const sal_Int32 nLen)
432 SvgUnit aRetval(Unit_px);
434 if(nPos < nLen)
436 const sal_Unicode aCharA(rCandidate[nPos]);
438 if(nPos + 1 < nLen)
440 const sal_Unicode aCharB(rCandidate[nPos + 1]);
441 bool bTwoCharValid(false);
443 switch(aCharA)
445 case u'e' :
447 if('m' == aCharB)
449 // 'em' Relative to current font size
450 aRetval = Unit_em;
451 bTwoCharValid = true;
453 else if('x' == aCharB)
455 // 'ex' Relative to current font x-height
456 aRetval = Unit_ex;
457 bTwoCharValid = true;
459 break;
461 case u'p' :
463 if('x' == aCharB)
465 // 'px' UserUnit (default)
466 bTwoCharValid = true;
468 else if('t' == aCharB)
470 // 'pt' == 4/3 px
471 aRetval = Unit_pt;
472 bTwoCharValid = true;
474 else if('c' == aCharB)
476 // 'pc' == 16 px
477 aRetval = Unit_pc;
478 bTwoCharValid = true;
480 break;
482 case u'i' :
484 if('n' == aCharB)
486 // 'in' == 96 px, since CSS 2.1
487 aRetval = Unit_in;
488 bTwoCharValid = true;
490 break;
492 case u'c' :
494 if('m' == aCharB)
496 // 'cm' == 37.79527559 px
497 aRetval = Unit_cm;
498 bTwoCharValid = true;
500 break;
502 case u'm' :
504 if('m' == aCharB)
506 // 'mm' == 3.779528 px
507 aRetval = Unit_mm;
508 bTwoCharValid = true;
510 break;
514 if(bTwoCharValid)
516 nPos += 2;
519 else
521 if('%' == aCharA)
523 // percent used, relative to current
524 nPos++;
525 aRetval = Unit_percent;
530 return aRetval;
533 bool readNumberAndUnit(const OUString& rCandidate, sal_Int32& nPos, SvgNumber& aNum, const sal_Int32 nLen)
535 double fNum(0.0);
537 if(readNumber(rCandidate, nPos, fNum, nLen))
539 skip_char(rCandidate, ' ', nPos, nLen);
540 aNum = SvgNumber(fNum, readUnit(rCandidate, nPos, nLen));
542 return true;
545 return false;
548 bool readAngle(const OUString& rCandidate, sal_Int32& nPos, double& fAngle, const sal_Int32 nLen)
550 if(readNumber(rCandidate, nPos, fAngle, nLen))
552 skip_char(rCandidate, ' ', nPos, nLen);
554 enum DegreeType
556 deg,
557 grad,
559 } aType(deg); // degrees is default
561 if(nPos < nLen)
563 const sal_Unicode aChar(rCandidate[nPos]);
564 static const char aStrGrad[] = "grad";
565 static const char aStrRad[] = "rad";
567 switch(aChar)
569 case u'g' :
570 case u'G' :
572 if(rCandidate.matchIgnoreAsciiCase(aStrGrad, nPos))
574 // angle in grad
575 nPos += strlen(aStrGrad);
576 aType = grad;
578 break;
580 case u'r' :
581 case u'R' :
583 if(rCandidate.matchIgnoreAsciiCase(aStrRad, nPos))
585 // angle in radians
586 nPos += strlen(aStrRad);
587 aType = rad;
589 break;
594 // convert to radians
595 if(deg == aType)
597 fAngle *= F_PI / 180.0;
599 else if(grad == aType)
601 // looks like 100 grad is 90 degrees
602 fAngle *= F_PI / 200.0;
605 return true;
608 return false;
611 sal_Int32 read_hex(sal_Unicode nChar)
613 if(nChar >= '0' && nChar <= '9')
615 return nChar - u'0';
617 else if(nChar >= 'A' && nChar <= 'F')
619 return 10 + sal_Int32(nChar - u'A');
621 else if(nChar >= 'a' && nChar <= 'f')
623 return 10 + sal_Int32(nChar - u'a');
625 else
627 // error
628 return 0;
632 bool match_colorKeyword(basegfx::BColor& rColor, const OUString& rName, bool bCaseIndependent)
634 typedef std::unordered_map< OUString, Color > ColorTokenMapper;
635 typedef std::pair< OUString, Color > ColorTokenValueType;
636 ColorTokenMapper aColorTokenMapperList;
638 if(aColorTokenMapperList.empty())
640 aColorTokenMapperList.insert(ColorTokenValueType(OUString("aliceblue"), Color(240, 248, 255)));
641 aColorTokenMapperList.insert(ColorTokenValueType(OUString("antiquewhite"), Color(250, 235, 215)));
642 aColorTokenMapperList.insert(ColorTokenValueType(OUString("aqua"), Color( 0, 255, 255)));
643 aColorTokenMapperList.insert(ColorTokenValueType(OUString("aquamarine"), Color(127, 255, 212)));
644 aColorTokenMapperList.insert(ColorTokenValueType(OUString("azure"), Color(240, 255, 255)));
645 aColorTokenMapperList.insert(ColorTokenValueType(OUString("beige"), Color(245, 245, 220)));
646 aColorTokenMapperList.insert(ColorTokenValueType(OUString("bisque"), Color(255, 228, 196)));
647 aColorTokenMapperList.insert(ColorTokenValueType(OUString("black"), Color( 0, 0, 0)));
648 aColorTokenMapperList.insert(ColorTokenValueType(OUString("blanchedalmond"), Color(255, 235, 205)));
649 aColorTokenMapperList.insert(ColorTokenValueType(OUString("blue"), Color( 0, 0, 255)));
650 aColorTokenMapperList.insert(ColorTokenValueType(OUString("blueviolet"), Color(138, 43, 226)));
651 aColorTokenMapperList.insert(ColorTokenValueType(OUString("brown"), Color(165, 42, 42)));
652 aColorTokenMapperList.insert(ColorTokenValueType(OUString("burlywood"), Color(222, 184, 135)));
653 aColorTokenMapperList.insert(ColorTokenValueType(OUString("cadetblue"), Color( 95, 158, 160)));
654 aColorTokenMapperList.insert(ColorTokenValueType(OUString("chartreuse"), Color(127, 255, 0)));
655 aColorTokenMapperList.insert(ColorTokenValueType(OUString("chocolate"), Color(210, 105, 30)));
656 aColorTokenMapperList.insert(ColorTokenValueType(OUString("coral"), Color(255, 127, 80)));
657 aColorTokenMapperList.insert(ColorTokenValueType(OUString("cornflowerblue"), Color(100, 149, 237)));
658 aColorTokenMapperList.insert(ColorTokenValueType(OUString("cornsilk"), Color(255, 248, 220)));
659 aColorTokenMapperList.insert(ColorTokenValueType(OUString("crimson"), Color(220, 20, 60)));
660 aColorTokenMapperList.insert(ColorTokenValueType(OUString("cyan"), Color( 0, 255, 255)));
661 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkblue"), Color( 0, 0, 139)));
662 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkcyan"), Color( 0, 139, 139)));
663 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkgoldenrod"), Color(184, 134, 11)));
664 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkgray"), Color(169, 169, 169)));
665 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkgreen"), Color( 0, 100, 0)));
666 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkgrey"), Color(169, 169, 169)));
667 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkkhaki"), Color(189, 183, 107)));
668 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkmagenta"), Color(139, 0, 139)));
669 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkolivegreen"), Color( 85, 107, 47)));
670 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkorange"), Color(255, 140, 0)));
671 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkorchid"), Color(153, 50, 204)));
672 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkred"), Color(139, 0, 0)));
673 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darksalmon"), Color(233, 150, 122)));
674 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkseagreen"), Color(143, 188, 143)));
675 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkslateblue"), Color( 72, 61, 139)));
676 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkslategray"), Color( 47, 79, 79)));
677 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkslategrey"), Color( 47, 79, 79)));
678 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkturquoise"), Color( 0, 206, 209)));
679 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkviolet"), Color(148, 0, 211)));
680 aColorTokenMapperList.insert(ColorTokenValueType(OUString("deeppink"), Color(255, 20, 147)));
681 aColorTokenMapperList.insert(ColorTokenValueType(OUString("deepskyblue"), Color( 0, 191, 255)));
682 aColorTokenMapperList.insert(ColorTokenValueType(OUString("dimgray"), Color(105, 105, 105)));
683 aColorTokenMapperList.insert(ColorTokenValueType(OUString("dimgrey"), Color(105, 105, 105)));
684 aColorTokenMapperList.insert(ColorTokenValueType(OUString("dodgerblue"), Color( 30, 144, 255)));
685 aColorTokenMapperList.insert(ColorTokenValueType(OUString("firebrick"), Color(178, 34, 34)));
686 aColorTokenMapperList.insert(ColorTokenValueType(OUString("floralwhite"), Color(255, 250, 240)));
687 aColorTokenMapperList.insert(ColorTokenValueType(OUString("forestgreen"), Color( 34, 139, 34)));
688 aColorTokenMapperList.insert(ColorTokenValueType(OUString("fuchsia"), Color(255, 0, 255)));
689 aColorTokenMapperList.insert(ColorTokenValueType(OUString("gainsboro"), Color(220, 220, 220)));
690 aColorTokenMapperList.insert(ColorTokenValueType(OUString("ghostwhite"), Color(248, 248, 255)));
691 aColorTokenMapperList.insert(ColorTokenValueType(OUString("gold"), Color(255, 215, 0)));
692 aColorTokenMapperList.insert(ColorTokenValueType(OUString("goldenrod"), Color(218, 165, 32)));
693 aColorTokenMapperList.insert(ColorTokenValueType(OUString("gray"), Color(128, 128, 128)));
694 aColorTokenMapperList.insert(ColorTokenValueType(OUString("grey"), Color(128, 128, 128)));
695 aColorTokenMapperList.insert(ColorTokenValueType(OUString("green"), Color(0, 128, 0)));
696 aColorTokenMapperList.insert(ColorTokenValueType(OUString("greenyellow"), Color(173, 255, 47)));
697 aColorTokenMapperList.insert(ColorTokenValueType(OUString("honeydew"), Color(240, 255, 240)));
698 aColorTokenMapperList.insert(ColorTokenValueType(OUString("hotpink"), Color(255, 105, 180)));
699 aColorTokenMapperList.insert(ColorTokenValueType(OUString("indianred"), Color(205, 92, 92)));
700 aColorTokenMapperList.insert(ColorTokenValueType(OUString("indigo"), Color( 75, 0, 130)));
701 aColorTokenMapperList.insert(ColorTokenValueType(OUString("ivory"), Color(255, 255, 240)));
702 aColorTokenMapperList.insert(ColorTokenValueType(OUString("khaki"), Color(240, 230, 140)));
703 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lavender"), Color(230, 230, 250)));
704 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lavenderblush"), Color(255, 240, 245)));
705 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lawngreen"), Color(124, 252, 0)));
706 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lemonchiffon"), Color(255, 250, 205)));
707 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightblue"), Color(173, 216, 230)));
708 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightcoral"), Color(240, 128, 128)));
709 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightcyan"), Color(224, 255, 255)));
710 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightgoldenrodyellow"), Color(250, 250, 210)));
711 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightgray"), Color(211, 211, 211)));
712 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightgreen"), Color(144, 238, 144)));
713 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightgrey"), Color(211, 211, 211)));
714 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightpink"), Color(255, 182, 193)));
715 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightsalmon"), Color(255, 160, 122)));
716 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightseagreen"), Color( 32, 178, 170)));
717 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightskyblue"), Color(135, 206, 250)));
718 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightslategray"), Color(119, 136, 153)));
719 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightslategrey"), Color(119, 136, 153)));
720 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightsteelblue"), Color(176, 196, 222)));
721 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightyellow"), Color(255, 255, 224)));
722 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lime"), Color( 0, 255, 0)));
723 aColorTokenMapperList.insert(ColorTokenValueType(OUString("limegreen"), Color( 50, 205, 50)));
724 aColorTokenMapperList.insert(ColorTokenValueType(OUString("linen"), Color(250, 240, 230)));
725 aColorTokenMapperList.insert(ColorTokenValueType(OUString("magenta"), Color(255, 0, 255)));
726 aColorTokenMapperList.insert(ColorTokenValueType(OUString("maroon"), Color(128, 0, 0)));
727 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumaquamarine"), Color(102, 205, 170)));
728 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumblue"), Color( 0, 0, 205)));
729 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumorchid"), Color(186, 85, 211)));
730 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumpurple"), Color(147, 112, 219)));
731 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumseagreen"), Color( 60, 179, 113)));
732 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumslateblue"), Color(123, 104, 238)));
733 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumspringgreen"), Color( 0, 250, 154)));
734 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumturquoise"), Color( 72, 209, 204)));
735 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumvioletred"), Color(199, 21, 133)));
736 aColorTokenMapperList.insert(ColorTokenValueType(OUString("midnightblue"), Color( 25, 25, 112)));
737 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mintcream"), Color(245, 255, 250)));
738 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mistyrose"), Color(255, 228, 225)));
739 aColorTokenMapperList.insert(ColorTokenValueType(OUString("moccasin"), Color(255, 228, 181)));
740 aColorTokenMapperList.insert(ColorTokenValueType(OUString("navajowhite"), Color(255, 222, 173)));
741 aColorTokenMapperList.insert(ColorTokenValueType(OUString("navy"), Color( 0, 0, 128)));
742 aColorTokenMapperList.insert(ColorTokenValueType(OUString("oldlace"), Color(253, 245, 230)));
743 aColorTokenMapperList.insert(ColorTokenValueType(OUString("olive"), Color(128, 128, 0)));
744 aColorTokenMapperList.insert(ColorTokenValueType(OUString("olivedrab"), Color(107, 142, 35)));
745 aColorTokenMapperList.insert(ColorTokenValueType(OUString("orange"), Color(255, 165, 0)));
746 aColorTokenMapperList.insert(ColorTokenValueType(OUString("orangered"), Color(255, 69, 0)));
747 aColorTokenMapperList.insert(ColorTokenValueType(OUString("orchid"), Color(218, 112, 214)));
748 aColorTokenMapperList.insert(ColorTokenValueType(OUString("palegoldenrod"), Color(238, 232, 170)));
749 aColorTokenMapperList.insert(ColorTokenValueType(OUString("palegreen"), Color(152, 251, 152)));
750 aColorTokenMapperList.insert(ColorTokenValueType(OUString("paleturquoise"), Color(175, 238, 238)));
751 aColorTokenMapperList.insert(ColorTokenValueType(OUString("palevioletred"), Color(219, 112, 147)));
752 aColorTokenMapperList.insert(ColorTokenValueType(OUString("papayawhip"), Color(255, 239, 213)));
753 aColorTokenMapperList.insert(ColorTokenValueType(OUString("peachpuff"), Color(255, 218, 185)));
754 aColorTokenMapperList.insert(ColorTokenValueType(OUString("peru"), Color(205, 133, 63)));
755 aColorTokenMapperList.insert(ColorTokenValueType(OUString("pink"), Color(255, 192, 203)));
756 aColorTokenMapperList.insert(ColorTokenValueType(OUString("plum"), Color(221, 160, 221)));
757 aColorTokenMapperList.insert(ColorTokenValueType(OUString("powderblue"), Color(176, 224, 230)));
758 aColorTokenMapperList.insert(ColorTokenValueType(OUString("purple"), Color(128, 0, 128)));
759 aColorTokenMapperList.insert(ColorTokenValueType(OUString("red"), Color(255, 0, 0)));
760 aColorTokenMapperList.insert(ColorTokenValueType(OUString("rosybrown"), Color(188, 143, 143)));
761 aColorTokenMapperList.insert(ColorTokenValueType(OUString("royalblue"), Color( 65, 105, 225)));
762 aColorTokenMapperList.insert(ColorTokenValueType(OUString("saddlebrown"), Color(139, 69, 19)));
763 aColorTokenMapperList.insert(ColorTokenValueType(OUString("salmon"), Color(250, 128, 114)));
764 aColorTokenMapperList.insert(ColorTokenValueType(OUString("sandybrown"), Color(244, 164, 96)));
765 aColorTokenMapperList.insert(ColorTokenValueType(OUString("seagreen"), Color( 46, 139, 87)));
766 aColorTokenMapperList.insert(ColorTokenValueType(OUString("seashell"), Color(255, 245, 238)));
767 aColorTokenMapperList.insert(ColorTokenValueType(OUString("sienna"), Color(160, 82, 45)));
768 aColorTokenMapperList.insert(ColorTokenValueType(OUString("silver"), Color(192, 192, 192)));
769 aColorTokenMapperList.insert(ColorTokenValueType(OUString("skyblue"), Color(135, 206, 235)));
770 aColorTokenMapperList.insert(ColorTokenValueType(OUString("slateblue"), Color(106, 90, 205)));
771 aColorTokenMapperList.insert(ColorTokenValueType(OUString("slategray"), Color(112, 128, 144)));
772 aColorTokenMapperList.insert(ColorTokenValueType(OUString("slategrey"), Color(112, 128, 144)));
773 aColorTokenMapperList.insert(ColorTokenValueType(OUString("snow"), Color(255, 250, 250)));
774 aColorTokenMapperList.insert(ColorTokenValueType(OUString("springgreen"), Color( 0, 255, 127)));
775 aColorTokenMapperList.insert(ColorTokenValueType(OUString("steelblue"), Color( 70, 130, 180)));
776 aColorTokenMapperList.insert(ColorTokenValueType(OUString("tan"), Color(210, 180, 140)));
777 aColorTokenMapperList.insert(ColorTokenValueType(OUString("teal"), Color( 0, 128, 128)));
778 aColorTokenMapperList.insert(ColorTokenValueType(OUString("thistle"), Color(216, 191, 216)));
779 aColorTokenMapperList.insert(ColorTokenValueType(OUString("tomato"), Color(255, 99, 71)));
780 aColorTokenMapperList.insert(ColorTokenValueType(OUString("turquoise"), Color( 64, 224, 208)));
781 aColorTokenMapperList.insert(ColorTokenValueType(OUString("violet"), Color(238, 130, 238)));
782 aColorTokenMapperList.insert(ColorTokenValueType(OUString("wheat"), Color(245, 222, 179)));
783 aColorTokenMapperList.insert(ColorTokenValueType(OUString("white"), Color(255, 255, 255)));
784 aColorTokenMapperList.insert(ColorTokenValueType(OUString("whitesmoke"), Color(245, 245, 245)));
785 aColorTokenMapperList.insert(ColorTokenValueType(OUString("yellow"), Color(255, 255, 0)));
786 aColorTokenMapperList.insert(ColorTokenValueType(OUString("yellowgreen"), Color(154, 205, 50)));
789 ColorTokenMapper::const_iterator aResult(aColorTokenMapperList.find(rName));
791 if(bCaseIndependent && aResult == aColorTokenMapperList.end())
793 // also try case independent match (e.g. for Css styles)
794 aResult = aColorTokenMapperList.find(rName.toAsciiLowerCase());
797 if(aResult == aColorTokenMapperList.end())
799 return false;
801 else
803 rColor = aResult->second.getBColor();
804 return true;
808 bool read_color(const OUString& rCandidate, basegfx::BColor& rColor, bool bCaseIndependent, SvgNumber& rOpacity)
810 const sal_Int32 nLen(rCandidate.getLength());
812 if(nLen)
814 const sal_Unicode aChar(rCandidate[0]);
815 const double fFactor(1.0 / 255.0);
817 if(aChar == '#')
819 // hex definition
820 OUStringBuffer aNum;
821 sal_Int32 nPos(1);
823 copyHex(rCandidate, nPos, aNum, nLen);
824 const sal_Int32 nLength(aNum.getLength());
826 if(3 == nLength)
828 const sal_Int32 nR(read_hex(aNum[0]));
829 const sal_Int32 nG(read_hex(aNum[1]));
830 const sal_Int32 nB(read_hex(aNum[2]));
832 rColor.setRed((nR | (nR << 4)) * fFactor);
833 rColor.setGreen((nG | (nG << 4)) * fFactor);
834 rColor.setBlue((nB | (nB << 4)) * fFactor);
836 return true;
838 else if(6 == nLength)
840 const sal_Int32 nR1(read_hex(aNum[0]));
841 const sal_Int32 nR2(read_hex(aNum[1]));
842 const sal_Int32 nG1(read_hex(aNum[2]));
843 const sal_Int32 nG2(read_hex(aNum[3]));
844 const sal_Int32 nB1(read_hex(aNum[4]));
845 const sal_Int32 nB2(read_hex(aNum[5]));
847 rColor.setRed((nR2 | (nR1 << 4)) * fFactor);
848 rColor.setGreen((nG2 | (nG1 << 4)) * fFactor);
849 rColor.setBlue((nB2 | (nB1 << 4)) * fFactor);
851 return true;
854 else
856 static const char aStrRgb[] = "rgb";
858 if(rCandidate.matchIgnoreAsciiCase(aStrRgb, 0))
860 // rgb/rgba definition
861 sal_Int32 nPos(strlen(aStrRgb));
862 bool bIsRGBA = false;
864 if('a' == rCandidate[nPos])
866 //Delete the 'a' from 'rbga'
867 skip_char(rCandidate, 'a', nPos, nPos + 1);
868 bIsRGBA = true;
871 skip_char(rCandidate, ' ', '(', nPos, nLen);
872 double fR(0.0);
874 if(readNumber(rCandidate, nPos, fR, nLen))
876 skip_char(rCandidate, ' ', nPos, nLen);
878 if(nPos < nLen)
880 const sal_Unicode aPercentChar(rCandidate[nPos]);
881 const bool bIsPercent('%' == aPercentChar);
882 double fG(0.0);
884 if(bIsPercent)
886 skip_char(rCandidate, '%', nPos, nLen);
889 skip_char(rCandidate, ' ', ',', nPos, nLen);
891 if(readNumber(rCandidate, nPos, fG, nLen))
893 double fB(0.0);
895 if(bIsPercent)
897 skip_char(rCandidate, '%', nPos, nLen);
900 skip_char(rCandidate, ' ', ',', nPos, nLen);
902 if(readNumber(rCandidate, nPos, fB, nLen))
904 double fA(1.0);
906 if(bIsPercent)
908 skip_char(rCandidate, '%', nPos, nLen);
911 skip_char(rCandidate, ' ', ',', nPos, nLen);
913 if(readNumber(rCandidate, nPos, fA, nLen))
915 if(bIsRGBA)
917 const double fFac(bIsPercent ? 0.01 : 1);
918 rOpacity = SvgNumber(fA * fFac);
920 if(bIsPercent)
922 skip_char(rCandidate, '%', nPos, nLen);
925 else
927 return false;
931 const double fFac(bIsPercent ? 0.01 : fFactor);
933 rColor.setRed(fR * fFac);
934 rColor.setGreen(fG * fFac);
935 rColor.setBlue(fB * fFac);
937 skip_char(rCandidate, ' ', ')', nPos, nLen);
938 return true;
944 else
946 // color keyword
947 if(match_colorKeyword(rColor, rCandidate, bCaseIndependent))
949 return true;
955 return false;
958 basegfx::B2DRange readViewBox(const OUString& rCandidate, InfoProvider const & rInfoProvider)
960 const sal_Int32 nLen(rCandidate.getLength());
962 if(nLen)
964 sal_Int32 nPos(0);
965 SvgNumber aMinX;
966 skip_char(rCandidate, ' ', ',', nPos, nLen);
968 if(readNumberAndUnit(rCandidate, nPos, aMinX, nLen))
970 SvgNumber aMinY;
971 skip_char(rCandidate, ' ', ',', nPos, nLen);
973 if(readNumberAndUnit(rCandidate, nPos, aMinY, nLen))
975 SvgNumber aWidth;
976 skip_char(rCandidate, ' ', ',', nPos, nLen);
978 if(readNumberAndUnit(rCandidate, nPos, aWidth, nLen))
980 SvgNumber aHeight;
981 skip_char(rCandidate, ' ', ',', nPos, nLen);
983 if(readNumberAndUnit(rCandidate, nPos, aHeight, nLen))
985 double fX(aMinX.solve(rInfoProvider, xcoordinate));
986 double fY(aMinY.solve(rInfoProvider, ycoordinate));
987 double fW(aWidth.solve(rInfoProvider,xcoordinate));
988 double fH(aHeight.solve(rInfoProvider,ycoordinate));
989 return basegfx::B2DRange(fX,fY,fX+fW,fY+fH);
996 return basegfx::B2DRange();
999 basegfx::B2DHomMatrix readTransform(const OUString& rCandidate, InfoProvider const & rInfoProvider)
1001 basegfx::B2DHomMatrix aMatrix;
1002 const sal_Int32 nLen(rCandidate.getLength());
1004 if(nLen)
1006 sal_Int32 nPos(0);
1007 skip_char(rCandidate, ' ', ',', nPos, nLen);
1009 while(nPos < nLen)
1011 const sal_Unicode aChar(rCandidate[nPos]);
1012 const sal_Int32 nInitPos(nPos);
1013 static const char aStrMatrix[] = "matrix";
1014 static const char aStrTranslate[] = "translate";
1015 static const char aStrScale[] = "scale";
1016 static const char aStrRotate[] = "rotate";
1017 static const char aStrSkewX[] = "skewX";
1018 static const char aStrSkewY[] = "skewY";
1020 switch(aChar)
1022 case u'm' :
1024 if(rCandidate.match(aStrMatrix, nPos))
1026 // matrix element
1027 nPos += strlen(aStrMatrix);
1028 skip_char(rCandidate, ' ', '(', nPos, nLen);
1029 SvgNumber aVal;
1030 basegfx::B2DHomMatrix aNew;
1032 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1034 aNew.set(0, 0, aVal.solve(rInfoProvider)); // Element A
1035 skip_char(rCandidate, ' ', ',', nPos, nLen);
1037 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1039 aNew.set(1, 0, aVal.solve(rInfoProvider)); // Element B
1040 skip_char(rCandidate, ' ', ',', nPos, nLen);
1042 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1044 aNew.set(0, 1, aVal.solve(rInfoProvider)); // Element C
1045 skip_char(rCandidate, ' ', ',', nPos, nLen);
1047 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1049 aNew.set(1, 1, aVal.solve(rInfoProvider)); // Element D
1050 skip_char(rCandidate, ' ', ',', nPos, nLen);
1052 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1054 aNew.set(0, 2, aVal.solve(rInfoProvider, xcoordinate)); // Element E
1055 skip_char(rCandidate, ' ', ',', nPos, nLen);
1057 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1059 aNew.set(1, 2, aVal.solve(rInfoProvider, ycoordinate)); // Element F
1060 skip_char(rCandidate, ' ', ')', nPos, nLen);
1061 skip_char(rCandidate, ' ', ',', nPos, nLen);
1063 // caution: String is evaluated from left to right, but matrix multiplication
1064 // in SVG is right to left, so put the new transformation before the current
1065 // one by multiplicating from the right side
1066 aMatrix = aMatrix * aNew;
1074 break;
1076 case u't' :
1078 if(rCandidate.match(aStrTranslate, nPos))
1080 // translate element
1081 nPos += strlen(aStrTranslate);
1082 skip_char(rCandidate, ' ', '(', nPos, nLen);
1083 SvgNumber aTransX;
1085 if(readNumberAndUnit(rCandidate, nPos, aTransX, nLen))
1087 skip_char(rCandidate, ' ', ',', nPos, nLen);
1088 SvgNumber aTransY;
1089 readNumberAndUnit(rCandidate, nPos, aTransY, nLen);
1090 skip_char(rCandidate, ' ', ')', nPos, nLen);
1091 skip_char(rCandidate, ' ', ',', nPos, nLen);
1093 aMatrix = aMatrix * basegfx::utils::createTranslateB2DHomMatrix(
1094 aTransX.solve(rInfoProvider, xcoordinate),
1095 aTransY.solve(rInfoProvider, ycoordinate));
1098 break;
1100 case u's' :
1102 if(rCandidate.match(aStrScale, nPos))
1104 // scale element
1105 nPos += strlen(aStrScale);
1106 skip_char(rCandidate, ' ', '(', nPos, nLen);
1107 SvgNumber aScaleX;
1109 if(readNumberAndUnit(rCandidate, nPos, aScaleX, nLen))
1111 skip_char(rCandidate, ' ', ',', nPos, nLen);
1112 SvgNumber aScaleY(aScaleX);
1113 readNumberAndUnit(rCandidate, nPos, aScaleY, nLen);
1114 skip_char(rCandidate, ' ', ')', nPos, nLen);
1115 skip_char(rCandidate, ' ', ',', nPos, nLen);
1117 aMatrix = aMatrix * basegfx::utils::createScaleB2DHomMatrix(
1118 aScaleX.solve(rInfoProvider),
1119 aScaleY.solve(rInfoProvider));
1122 else if(rCandidate.match(aStrSkewX, nPos))
1124 // skewx element
1125 nPos += strlen(aStrSkewX);
1126 skip_char(rCandidate, ' ', '(', nPos, nLen);
1127 double fSkewX(0.0);
1129 if(readAngle(rCandidate, nPos, fSkewX, nLen))
1131 skip_char(rCandidate, ' ', ')', nPos, nLen);
1132 skip_char(rCandidate, ' ', ',', nPos, nLen);
1134 aMatrix = aMatrix * basegfx::utils::createShearXB2DHomMatrix(tan(fSkewX));
1137 else if(rCandidate.match(aStrSkewY, nPos))
1139 // skewy element
1140 nPos += strlen(aStrSkewY);
1141 skip_char(rCandidate, ' ', '(', nPos, nLen);
1142 double fSkewY(0.0);
1144 if(readAngle(rCandidate, nPos, fSkewY, nLen))
1146 skip_char(rCandidate, ' ', ')', nPos, nLen);
1147 skip_char(rCandidate, ' ', ',', nPos, nLen);
1149 aMatrix = aMatrix * basegfx::utils::createShearYB2DHomMatrix(tan(fSkewY));
1152 break;
1154 case u'r' :
1156 if(rCandidate.match(aStrRotate, nPos))
1158 // rotate element
1159 nPos += strlen(aStrRotate);
1160 skip_char(rCandidate, ' ', '(', nPos, nLen);
1161 double fAngle(0.0);
1163 if(readAngle(rCandidate, nPos, fAngle, nLen))
1165 skip_char(rCandidate, ' ', ',', nPos, nLen);
1166 SvgNumber aX;
1167 readNumberAndUnit(rCandidate, nPos, aX, nLen);
1168 skip_char(rCandidate, ' ', ',', nPos, nLen);
1169 SvgNumber aY;
1170 readNumberAndUnit(rCandidate, nPos, aY, nLen);
1171 skip_char(rCandidate, ' ', ')', nPos, nLen);
1172 skip_char(rCandidate, ' ', ',', nPos, nLen);
1174 const double fX(aX.isSet() ? aX.solve(rInfoProvider, xcoordinate) : 0.0);
1175 const double fY(aY.isSet() ? aY.solve(rInfoProvider, ycoordinate) : 0.0);
1177 if(!basegfx::fTools::equalZero(fX) || !basegfx::fTools::equalZero(fY))
1179 // rotate around point
1180 aMatrix = aMatrix * basegfx::utils::createRotateAroundPoint(fX, fY, fAngle);
1182 else
1184 // rotate
1185 aMatrix = aMatrix * basegfx::utils::createRotateB2DHomMatrix(fAngle);
1189 break;
1193 if(nInitPos == nPos)
1195 OSL_ENSURE(false, "Could not interpret on current position (!)");
1196 nPos++;
1201 return aMatrix;
1204 bool readSingleNumber(const OUString& rCandidate, SvgNumber& aNum)
1206 const sal_Int32 nLen(rCandidate.getLength());
1207 sal_Int32 nPos(0);
1209 return readNumberAndUnit(rCandidate, nPos, aNum, nLen);
1212 bool readLocalUrl(const OUString& rCandidate, OUString& rURL)
1214 static const char aStrUrl[] = "url";
1216 if(rCandidate.startsWith(aStrUrl))
1218 const sal_Int32 nLen(rCandidate.getLength());
1219 sal_Int32 nPos(strlen(aStrUrl));
1221 skip_char(rCandidate, '(', '#', nPos, nLen);
1222 OUStringBuffer aTokenValue;
1223 copyToLimiter(rCandidate, ')', nPos, aTokenValue, nLen);
1224 rURL = aTokenValue.makeStringAndClear();
1226 return true;
1229 return false;
1232 bool readSvgPaint(const OUString& rCandidate, SvgPaint& rSvgPaint,
1233 OUString& rURL, bool bCaseIndependent, SvgNumber& rOpacity)
1235 if( !rCandidate.isEmpty() )
1237 basegfx::BColor aColor;
1239 if(read_color(rCandidate, aColor, bCaseIndependent, rOpacity))
1241 rSvgPaint = SvgPaint(aColor, true, true);
1242 return true;
1244 else
1246 if(rCandidate.startsWith("none"))
1248 rSvgPaint = SvgPaint(aColor, true, false, false);
1249 return true;
1251 else if(readLocalUrl(rCandidate, rURL))
1253 /// Url is copied to rURL, but needs to be solved outside this helper
1254 return false;
1256 else if(rCandidate.startsWith("currentColor"))
1258 rSvgPaint = SvgPaint(aColor, true, true, true);
1259 return true;
1264 return false;
1267 bool readSvgNumberVector(const OUString& rCandidate, SvgNumberVector& rSvgNumberVector)
1269 const sal_Int32 nLen(rCandidate.getLength());
1270 rSvgNumberVector.clear();
1272 if(nLen)
1274 sal_Int32 nPos(0);
1275 SvgNumber aNum;
1276 skip_char(rCandidate, ' ', ',', nPos, nLen);
1278 while(readNumberAndUnit(rCandidate, nPos, aNum, nLen))
1280 rSvgNumberVector.push_back(aNum);
1281 skip_char(rCandidate, ' ', ',', nPos, nLen);
1284 return !rSvgNumberVector.empty();
1287 return false;
1290 SvgAspectRatio readSvgAspectRatio(const OUString& rCandidate)
1292 const sal_Int32 nLen(rCandidate.getLength());
1294 if(nLen)
1296 sal_Int32 nPos(0);
1297 SvgAlign aSvgAlign(Align_xMidYMid);
1298 bool bMeetOrSlice(true);
1299 bool bChanged(false);
1301 while(nPos < nLen)
1303 const sal_Int32 nInitPos(nPos);
1304 skip_char(rCandidate, ' ', nPos, nLen);
1305 OUStringBuffer aTokenName;
1306 copyString(rCandidate, nPos, aTokenName, nLen);
1308 if(!aTokenName.isEmpty())
1310 switch(StrToSVGToken(aTokenName.makeStringAndClear(), false))
1312 case SVGTokenDefer:
1314 bChanged = true;
1315 break;
1317 case SVGTokenNone:
1319 aSvgAlign = Align_none;
1320 bChanged = true;
1321 break;
1323 case SVGTokenXMinYMin:
1325 aSvgAlign = Align_xMinYMin;
1326 bChanged = true;
1327 break;
1329 case SVGTokenXMidYMin:
1331 aSvgAlign = Align_xMidYMin;
1332 bChanged = true;
1333 break;
1335 case SVGTokenXMaxYMin:
1337 aSvgAlign = Align_xMaxYMin;
1338 bChanged = true;
1339 break;
1341 case SVGTokenXMinYMid:
1343 aSvgAlign = Align_xMinYMid;
1344 bChanged = true;
1345 break;
1347 case SVGTokenXMidYMid:
1349 aSvgAlign = Align_xMidYMid;
1350 bChanged = true;
1351 break;
1353 case SVGTokenXMaxYMid:
1355 aSvgAlign = Align_xMaxYMid;
1356 bChanged = true;
1357 break;
1359 case SVGTokenXMinYMax:
1361 aSvgAlign = Align_xMinYMax;
1362 bChanged = true;
1363 break;
1365 case SVGTokenXMidYMax:
1367 aSvgAlign = Align_xMidYMax;
1368 bChanged = true;
1369 break;
1371 case SVGTokenXMaxYMax:
1373 aSvgAlign = Align_xMaxYMax;
1374 bChanged = true;
1375 break;
1377 case SVGTokenMeet:
1379 bMeetOrSlice = true;
1380 bChanged = true;
1381 break;
1383 case SVGTokenSlice:
1385 bMeetOrSlice = false;
1386 bChanged = true;
1387 break;
1389 default:
1391 break;
1396 if(nInitPos == nPos)
1398 OSL_ENSURE(false, "Could not interpret on current position (!)");
1399 nPos++;
1403 if(bChanged)
1405 return SvgAspectRatio(aSvgAlign, bMeetOrSlice);
1409 return SvgAspectRatio();
1412 bool readSvgStringVector(const OUString& rCandidate, SvgStringVector& rSvgStringVector)
1414 rSvgStringVector.clear();
1415 const sal_Int32 nLen(rCandidate.getLength());
1417 if(nLen)
1419 sal_Int32 nPos(0);
1420 OUStringBuffer aTokenValue;
1421 skip_char(rCandidate, ' ', ',', nPos, nLen);
1423 while(nPos < nLen)
1425 copyToLimiter(rCandidate, ',', nPos, aTokenValue, nLen);
1426 skip_char(rCandidate, ',', ' ', nPos, nLen);
1427 const OUString aString = aTokenValue.makeStringAndClear();
1429 if(!aString.isEmpty())
1431 rSvgStringVector.push_back(aString);
1436 return !rSvgStringVector.empty();
1439 void readImageLink(const OUString& rCandidate, OUString& rXLink, OUString& rUrl, OUString& rMimeType, OUString& rData)
1441 rXLink.clear();
1442 rUrl.clear();
1443 rMimeType.clear();
1444 rData.clear();
1446 if('#' == rCandidate[0])
1448 // local link
1449 rXLink = rCandidate.copy(1);
1451 else
1453 static const char aStrData[] = "data:";
1455 if(rCandidate.match(aStrData, 0))
1457 // embedded data
1458 sal_Int32 nPos(strlen(aStrData));
1459 sal_Int32 nLen(rCandidate.getLength());
1460 OUStringBuffer aBuffer;
1462 // read mime type
1463 skip_char(rCandidate, ' ', nPos, nLen);
1464 copyToLimiter(rCandidate, ';', nPos, aBuffer, nLen);
1465 skip_char(rCandidate, ' ', ';', nPos, nLen);
1466 rMimeType = aBuffer.makeStringAndClear();
1468 if(!rMimeType.isEmpty() && nPos < nLen)
1470 if(rMimeType.startsWith("image"))
1472 // image data
1473 OUString aData(rCandidate.copy(nPos));
1474 static const char aStrBase64[] = "base64";
1476 if(aData.startsWith(aStrBase64))
1478 // base64 encoded
1479 nPos = strlen(aStrBase64);
1480 nLen = aData.getLength();
1482 skip_char(aData, ' ', ',', nPos, nLen);
1484 if(nPos < nLen)
1486 rData = aData.copy(nPos);
1492 else
1494 // Url (path and filename)
1495 rUrl = rCandidate;
1500 OUString convert(const OUString& rCandidate, sal_Unicode nPattern, sal_Unicode nNew, bool bRemove)
1502 const sal_Int32 nLen(rCandidate.getLength());
1504 if(nLen)
1506 sal_Int32 nPos(0);
1507 OUStringBuffer aBuffer;
1508 bool bChanged(false);
1510 while(nPos < nLen)
1512 const sal_Unicode aChar(rCandidate[nPos]);
1514 if(nPattern == aChar)
1516 bChanged = true;
1518 if(!bRemove)
1520 aBuffer.append(nNew);
1523 else
1525 aBuffer.append(aChar);
1528 nPos++;
1531 if(bChanged)
1533 return aBuffer.makeStringAndClear();
1537 return rCandidate;
1540 // #i125325#
1541 OUString removeBlockComments(const OUString& rCandidate)
1543 const sal_Int32 nLen(rCandidate.getLength());
1545 if(nLen)
1547 sal_Int32 nPos(0);
1548 OUStringBuffer aBuffer;
1549 bool bChanged(false);
1550 sal_Int32 nInsideComment(0);
1551 const sal_Unicode aCommentSlash('/');
1552 const sal_Unicode aCommentStar('*');
1554 while(nPos < nLen)
1556 const sal_Unicode aChar(rCandidate[nPos]);
1557 const bool bStart(aCommentSlash == aChar && nPos + 1 < nLen && aCommentStar == rCandidate[nPos + 1]);
1558 const bool bEnd(aCommentStar == aChar && nPos + 1 < nLen && aCommentSlash == rCandidate[nPos + 1]);
1560 if(bStart)
1562 nPos += 2;
1563 nInsideComment++;
1564 bChanged = true;
1566 else if(bEnd)
1568 nPos += 2;
1569 nInsideComment--;
1571 else
1573 if(!nInsideComment)
1575 aBuffer.append(aChar);
1578 nPos++;
1582 if(bChanged)
1584 return aBuffer.makeStringAndClear();
1588 return rCandidate;
1591 OUString consolidateContiguousSpace(const OUString& rCandidate)
1593 const sal_Int32 nLen(rCandidate.getLength());
1595 if(nLen)
1597 sal_Int32 nPos(0);
1598 OUStringBuffer aBuffer;
1599 bool bInsideSpace(false);
1600 const sal_Unicode aSpace(' ');
1602 while(nPos < nLen)
1604 const sal_Unicode aChar(rCandidate[nPos]);
1606 if(aSpace == aChar)
1608 bInsideSpace = true;
1610 else
1612 if(bInsideSpace)
1614 bInsideSpace = false;
1615 aBuffer.append(aSpace);
1618 aBuffer.append(aChar);
1621 nPos++;
1624 if(bInsideSpace)
1626 aBuffer.append(aSpace);
1629 if(aBuffer.getLength() != nLen)
1631 return aBuffer.makeStringAndClear();
1635 return rCandidate;
1638 OUString whiteSpaceHandlingDefault(const OUString& rCandidate)
1640 const sal_Unicode aNewline('\n');
1641 const sal_Unicode aTab('\t');
1642 const sal_Unicode aSpace(' ');
1644 // remove all newline characters
1645 OUString aRetval(convert(rCandidate, aNewline, aNewline, true));
1647 // convert tab to space
1648 aRetval = convert(aRetval, aTab, aSpace, false);
1650 // strip of all leading and trailing spaces
1651 aRetval = aRetval.trim();
1653 // consolidate contiguous space
1654 aRetval = consolidateContiguousSpace(aRetval);
1656 return aRetval;
1659 OUString whiteSpaceHandlingPreserve(const OUString& rCandidate)
1661 const sal_Unicode aNewline('\n');
1662 const sal_Unicode aTab('\t');
1663 const sal_Unicode aSpace(' ');
1665 // convert newline to space
1666 convert(rCandidate, aNewline, aSpace, false);
1668 // convert tab to space
1669 convert(rCandidate, aTab, aSpace, false);
1671 return rCandidate;
1674 ::std::vector< double > solveSvgNumberVector(const SvgNumberVector& rInput, const InfoProvider& rInfoProvider)
1676 ::std::vector< double > aRetval;
1678 if(!rInput.empty())
1680 const double nCount(rInput.size());
1681 aRetval.reserve(nCount);
1683 for(sal_uInt32 a(0); a < nCount; a++)
1685 aRetval.push_back(rInput[a].solve(rInfoProvider));
1689 return aRetval;
1692 } // end of namespace svgreader
1693 } // end of namespace svgio
1695 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */