Updated core
[LibreOffice.git] / svgio / source / svgreader / svgtools.cxx
blob5ae5830c2996c9c6d0bae044cca48b97b76d253e
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 <svgio/svgreader/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 <svgio/svgreader/svgtoken.hxx>
26 #include <unordered_map>
28 namespace svgio
30 namespace svgreader
32 #ifdef DBG_UTIL
33 void myAssert(const OUString& rMessage)
35 OString aMessage2;
37 rMessage.convertToString(&aMessage2, osl_getThreadTextEncoding(), RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR|RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR);
38 OSL_ENSURE(false, aMessage2.getStr());
40 #endif
42 // common non-token strings
43 const OUString commonStrings::aStrUserSpaceOnUse("userSpaceOnUse");
44 const OUString commonStrings::aStrObjectBoundingBox("objectBoundingBox");
45 const OUString commonStrings::aStrNonzero("nonzero");
46 const OUString commonStrings::aStrEvenOdd("evenodd");
48 basegfx::B2DHomMatrix SvgAspectRatio::createLinearMapping(const basegfx::B2DRange& rTarget, const basegfx::B2DRange& rSource)
50 basegfx::B2DHomMatrix aRetval;
51 const double fSWidth(rSource.getWidth());
52 const double fSHeight(rSource.getHeight());
53 const bool bNoSWidth(basegfx::fTools::equalZero(fSWidth));
54 const bool bNoSHeight(basegfx::fTools::equalZero(fSHeight));
56 // transform from source state to unit range
57 aRetval.translate(-rSource.getMinX(), -rSource.getMinY());
58 aRetval.scale(
59 (bNoSWidth ? 1.0 : 1.0 / fSWidth) * rTarget.getWidth(),
60 (bNoSHeight ? 1.0 : 1.0 / fSHeight) * rTarget.getHeight());
62 // transform from unit rage to target range
63 aRetval.translate(rTarget.getMinX(), rTarget.getMinY());
65 return aRetval;
68 basegfx::B2DHomMatrix SvgAspectRatio::createMapping(const basegfx::B2DRange& rTarget, const basegfx::B2DRange& rSource) const
70 if(!isSet() || Align_none == getSvgAlign())
72 // create linear mapping (default)
73 return createLinearMapping(rTarget, rSource);
76 basegfx::B2DHomMatrix aRetval;
78 const double fSWidth(rSource.getWidth());
79 const double fSHeight(rSource.getHeight());
80 const bool bNoSWidth(basegfx::fTools::equalZero(fSWidth));
81 const bool bNoSHeight(basegfx::fTools::equalZero(fSHeight));
82 const double fScaleX((bNoSWidth ? 1.0 : 1.0 / fSWidth) * rTarget.getWidth());
83 const double fScaleY((bNoSHeight ? 1.0 : 1.0 / fSHeight) * rTarget.getHeight());
84 const double fScale(isMeetOrSlice() ? std::min(fScaleX, fScaleY) : std::max(fScaleX, fScaleY));
86 // remove source translation, apply scale
87 aRetval.translate(-rSource.getMinX(), -rSource.getMinY());
88 aRetval.scale(fScale, fScale);
90 // evaluate horizontal alignment
91 const double fNewWidth(fSWidth * fScale);
92 double fTransX(0.0);
94 switch(getSvgAlign())
96 case Align_xMidYMin:
97 case Align_xMidYMid:
98 case Align_xMidYMax:
100 // centerX
101 const double fFreeSpace(rTarget.getWidth() - fNewWidth);
102 fTransX = fFreeSpace * 0.5;
103 break;
105 case Align_xMaxYMin:
106 case Align_xMaxYMid:
107 case Align_xMaxYMax:
109 // Right align
110 const double fFreeSpace(rTarget.getWidth() - fNewWidth);
111 fTransX = fFreeSpace;
112 break;
114 default: break;
117 // evaluate vertical alignment
118 const double fNewHeight(fSHeight * fScale);
119 double fTransY(0.0);
121 switch(getSvgAlign())
123 case Align_xMinYMid:
124 case Align_xMidYMid:
125 case Align_xMaxYMid:
127 // centerY
128 const double fFreeSpace(rTarget.getHeight() - fNewHeight);
129 fTransY = fFreeSpace * 0.5;
130 break;
132 case Align_xMinYMax:
133 case Align_xMidYMax:
134 case Align_xMaxYMax:
136 // Bottom align
137 const double fFreeSpace(rTarget.getHeight() - fNewHeight);
138 fTransY = fFreeSpace;
139 break;
141 default: break;
144 // add target translation
145 aRetval.translate(
146 rTarget.getMinX() + fTransX,
147 rTarget.getMinY() + fTransY);
149 return aRetval;
152 double SvgNumber::solveNonPercentage(const InfoProvider& rInfoProvider) const
154 if(isSet())
156 switch(meUnit)
158 case Unit_em:
160 return mfNumber * rInfoProvider.getCurrentFontSizeInherited();
162 case Unit_ex:
164 return mfNumber * rInfoProvider.getCurrentXHeightInherited() * 0.5;
166 case Unit_px:
168 return mfNumber;
170 case Unit_pt:
171 case Unit_pc:
172 case Unit_cm:
173 case Unit_mm:
174 case Unit_in:
176 double fRetval(mfNumber);
178 switch(meUnit)
180 case Unit_pt: fRetval *= F_SVG_PIXEL_PER_INCH / 72.0; break;
181 case Unit_pc: fRetval *= F_SVG_PIXEL_PER_INCH / 6.0; break;
182 case Unit_cm: fRetval *= F_SVG_PIXEL_PER_INCH / 2.54; break;
183 case Unit_mm: fRetval *= 0.1 * F_SVG_PIXEL_PER_INCH / 2.54; break;
184 case Unit_in: fRetval *= F_SVG_PIXEL_PER_INCH; break;
185 default: break;
188 return fRetval;
190 default:
192 OSL_ENSURE(false, "Do not use with percentage! ");
193 return 0.0;
198 /// not set
199 OSL_ENSURE(false, "SvgNumber not set (!)");
200 return 0.0;
203 double SvgNumber::solve(const InfoProvider& rInfoProvider, NumberType aNumberType) const
205 if(isSet())
207 switch(meUnit)
209 case Unit_px:
211 return mfNumber;
213 case Unit_pt:
214 case Unit_pc:
215 case Unit_cm:
216 case Unit_mm:
217 case Unit_in:
218 case Unit_em:
219 case Unit_ex:
221 return solveNonPercentage( rInfoProvider);
223 case Unit_percent:
225 double fRetval(mfNumber * 0.01);
226 basegfx::B2DRange aViewPort = rInfoProvider.getCurrentViewPort();
228 if ( aViewPort.isEmpty() )
230 #ifdef DBG_UTIL
231 myAssert(OUString("Design error, this case should have been handled in the caller"));
232 #endif
233 // no viewPort, assume a normal page size (A4)
234 aViewPort = basegfx::B2DRange(
235 0.0,
236 0.0,
237 210.0 * F_SVG_PIXEL_PER_INCH / 2.54,
238 297.0 * F_SVG_PIXEL_PER_INCH / 2.54);
242 if ( !aViewPort.isEmpty() )
244 if(xcoordinate == aNumberType)
246 // it's a x-coordinate, relative to current width (w)
247 fRetval *= aViewPort.getWidth();
249 else if(ycoordinate == aNumberType)
251 // it's a y-coordinate, relative to current height (h)
252 fRetval *= aViewPort.getHeight();
254 else // length
256 // it's a length, relative to sqrt(w*w + h*h)/sqrt(2)
257 const double fCurrentWidth(aViewPort.getWidth());
258 const double fCurrentHeight(aViewPort.getHeight());
259 const double fCurrentLength(
260 sqrt(fCurrentWidth * fCurrentWidth + fCurrentHeight * fCurrentHeight)/sqrt(2.0));
262 fRetval *= fCurrentLength;
266 return fRetval;
268 default:
270 break;
275 /// not set
276 OSL_ENSURE(false, "SvgNumber not set (!)");
277 return 0.0;
280 bool SvgNumber::isPositive() const
282 return basegfx::fTools::moreOrEqual(mfNumber, 0.0);
285 void skip_char(const OUString& rCandidate, const sal_Unicode& rChar, sal_Int32& nPos, const sal_Int32 nLen)
287 while(nPos < nLen && rChar == rCandidate[nPos])
289 nPos++;
293 void skip_char(const OUString& rCandidate, const sal_Unicode& rCharA, const sal_Unicode& rCharB, sal_Int32& nPos, const sal_Int32 nLen)
295 while(nPos < nLen && (rCharA == rCandidate[nPos] || rCharB == rCandidate[nPos]))
297 nPos++;
301 void copySign(const OUString& rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
303 if(nPos < nLen)
305 const sal_Unicode aChar(rCandidate[nPos]);
307 if('+' == aChar || '-' == aChar)
309 rTarget.append(aChar);
310 nPos++;
315 void copyNumber(const OUString& rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
317 bool bOnNumber(true);
319 while(bOnNumber && nPos < nLen)
321 const sal_Unicode aChar(rCandidate[nPos]);
323 bOnNumber = ('0' <= aChar && '9' >= aChar) || '.' == aChar;
325 if(bOnNumber)
327 rTarget.append(aChar);
328 nPos++;
333 void copyHex(const OUString& rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
335 bool bOnHex(true);
337 while(bOnHex && nPos < nLen)
339 const sal_Unicode aChar(rCandidate[nPos]);
341 bOnHex = ('0' <= aChar && '9' >= aChar)
342 || ('A' <= aChar && 'F' >= aChar)
343 || ('a' <= aChar && 'f' >= aChar);
345 if(bOnHex)
347 rTarget.append(aChar);
348 nPos++;
353 void copyString(const OUString& rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
355 bool bOnChar(true);
357 while(bOnChar && nPos < nLen)
359 const sal_Unicode aChar(rCandidate[nPos]);
361 bOnChar = ('a' <= aChar && 'z' >= aChar)
362 || ('A' <= aChar && 'Z' >= aChar)
363 || '-' == aChar;
365 if(bOnChar)
367 rTarget.append(aChar);
368 nPos++;
373 void copyToLimiter(const OUString& rCandidate, const sal_Unicode& rLimiter, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
375 while(nPos < nLen && rLimiter != rCandidate[nPos])
377 rTarget.append(rCandidate[nPos]);
378 nPos++;
382 bool readNumber(const OUString& rCandidate, sal_Int32& nPos, double& fNum, const sal_Int32 nLen)
384 if(nPos < nLen)
386 OUStringBuffer aNum;
388 copySign(rCandidate, nPos, aNum, nLen);
389 copyNumber(rCandidate, nPos, aNum, nLen);
391 if(nPos < nLen)
393 const sal_Unicode aChar(rCandidate[nPos]);
395 if('e' == aChar || 'E' == aChar)
397 // try to read exponential number, but be careful. I had
398 // a case where dx="2em" was used, thus the 'e' was consumed
399 // by error. First try if there are numbers after the 'e',
400 // safe current state
401 nPos++;
402 const OUStringBuffer aNum2(aNum);
403 const sal_Int32 nPosAfterE(nPos);
405 aNum.append(aChar);
406 copySign(rCandidate, nPos, aNum, nLen);
407 copyNumber(rCandidate, nPos, aNum, nLen);
409 if(nPosAfterE == nPos)
411 // no number after 'e', go back. Do not
412 // return false, it's still a valid integer number
413 aNum = aNum2;
414 nPos--;
419 if(!aNum.isEmpty())
421 rtl_math_ConversionStatus eStatus;
423 fNum = rtl::math::stringToDouble(
424 aNum.makeStringAndClear(), '.', ',',
425 &eStatus, 0);
427 return eStatus == rtl_math_ConversionStatus_Ok;
431 return false;
434 SvgUnit readUnit(const OUString& rCandidate, sal_Int32& nPos, const sal_Int32 nLen)
436 SvgUnit aRetval(Unit_px);
438 if(nPos < nLen)
440 const sal_Unicode aCharA(rCandidate[nPos]);
442 if(nPos + 1 < nLen)
444 const sal_Unicode aCharB(rCandidate[nPos + 1]);
445 bool bTwoCharValid(false);
447 switch(aCharA)
449 case sal_Unicode('e') :
451 if('m' == aCharB)
453 // 'em' Relative to current font size
454 aRetval = Unit_em;
455 bTwoCharValid = true;
457 else if('x' == aCharB)
459 // 'ex' Relative to current font x-height
460 aRetval = Unit_ex;
461 bTwoCharValid = true;
463 break;
465 case sal_Unicode('p') :
467 if('x' == aCharB)
469 // 'px' UserUnit (default)
470 bTwoCharValid = true;
472 else if('t' == aCharB)
474 // 'pt' == 1.25 px
475 aRetval = Unit_pt;
476 bTwoCharValid = true;
478 else if('c' == aCharB)
480 // 'pc' == 15 px
481 aRetval = Unit_pc;
482 bTwoCharValid = true;
484 break;
486 case sal_Unicode('i') :
488 if('n' == aCharB)
490 // 'in' == 90 px
491 aRetval = Unit_in;
492 bTwoCharValid = true;
494 break;
496 case sal_Unicode('c') :
498 if('m' == aCharB)
500 // 'cm' == 35.43307 px
501 aRetval = Unit_cm;
502 bTwoCharValid = true;
504 break;
506 case sal_Unicode('m') :
508 if('m' == aCharB)
510 // 'mm' == 3.543307 px
511 aRetval = Unit_mm;
512 bTwoCharValid = true;
514 break;
518 if(bTwoCharValid)
520 nPos += 2;
523 else
525 if('%' == aCharA)
527 // percent used, relative to current
528 nPos++;
529 aRetval = Unit_percent;
534 return aRetval;
537 bool readNumberAndUnit(const OUString& rCandidate, sal_Int32& nPos, SvgNumber& aNum, const sal_Int32 nLen)
539 double fNum(0.0);
541 if(readNumber(rCandidate, nPos, fNum, nLen))
543 skip_char(rCandidate, ' ', nPos, nLen);
544 aNum = SvgNumber(fNum, readUnit(rCandidate, nPos, nLen));
546 return true;
549 return false;
552 bool readAngle(const OUString& rCandidate, sal_Int32& nPos, double& fAngle, const sal_Int32 nLen)
554 if(readNumber(rCandidate, nPos, fAngle, nLen))
556 skip_char(rCandidate, ' ', nPos, nLen);
558 enum DegreeType
560 deg,
561 grad,
563 } aType(deg); // degrees is default
565 if(nPos < nLen)
567 const sal_Unicode aChar(rCandidate[nPos]);
568 static const char aStrGrad[] = "grad";
569 static const char aStrRad[] = "rad";
571 switch(aChar)
573 case sal_Unicode('g') :
574 case sal_Unicode('G') :
576 if(rCandidate.matchIgnoreAsciiCase(aStrGrad, nPos))
578 // angle in grad
579 nPos += strlen(aStrGrad);
580 aType = grad;
582 break;
584 case sal_Unicode('r') :
585 case sal_Unicode('R') :
587 if(rCandidate.matchIgnoreAsciiCase(aStrRad, nPos))
589 // angle in radians
590 nPos += strlen(aStrRad);
591 aType = rad;
593 break;
598 // convert to radians
599 if(deg == aType)
601 fAngle *= F_PI / 180.0;
603 else if(grad == aType)
605 // looks like 100 grad is 90 degrees
606 fAngle *= F_PI / 200.0;
609 return true;
612 return false;
615 sal_Int32 read_hex(const sal_Unicode& rChar)
617 if(rChar >= '0' && rChar <= '9')
619 return sal_Int32(rChar - sal_Unicode('0'));
621 else if(rChar >= 'A' && rChar <= 'F')
623 return 10 + sal_Int32(rChar - sal_Unicode('A'));
625 else if(rChar >= 'a' && rChar <= 'f')
627 return 10 + sal_Int32(rChar - sal_Unicode('a'));
629 else
631 // error
632 return 0;
636 bool match_colorKeyword(basegfx::BColor& rColor, const OUString& rName, bool bCaseIndependent)
638 typedef std::unordered_map< OUString, Color,
639 OUStringHash,
640 ::std::equal_to< OUString >
641 > ColorTokenMapper;
642 typedef std::pair< OUString, Color > ColorTokenValueType;
643 ColorTokenMapper aColorTokenMapperList;
645 if(aColorTokenMapperList.empty())
647 aColorTokenMapperList.insert(ColorTokenValueType(OUString("aliceblue"), Color(240, 248, 255)));
648 aColorTokenMapperList.insert(ColorTokenValueType(OUString("antiquewhite"), Color(250, 235, 215)));
649 aColorTokenMapperList.insert(ColorTokenValueType(OUString("aqua"), Color( 0, 255, 255)));
650 aColorTokenMapperList.insert(ColorTokenValueType(OUString("aquamarine"), Color(127, 255, 212)));
651 aColorTokenMapperList.insert(ColorTokenValueType(OUString("azure"), Color(240, 255, 255)));
652 aColorTokenMapperList.insert(ColorTokenValueType(OUString("beige"), Color(245, 245, 220)));
653 aColorTokenMapperList.insert(ColorTokenValueType(OUString("bisque"), Color(255, 228, 196)));
654 aColorTokenMapperList.insert(ColorTokenValueType(OUString("black"), Color( 0, 0, 0)));
655 aColorTokenMapperList.insert(ColorTokenValueType(OUString("blanchedalmond"), Color(255, 235, 205)));
656 aColorTokenMapperList.insert(ColorTokenValueType(OUString("blue"), Color( 0, 0, 255)));
657 aColorTokenMapperList.insert(ColorTokenValueType(OUString("blueviolet"), Color(138, 43, 226)));
658 aColorTokenMapperList.insert(ColorTokenValueType(OUString("brown"), Color(165, 42, 42)));
659 aColorTokenMapperList.insert(ColorTokenValueType(OUString("burlywood"), Color(222, 184, 135)));
660 aColorTokenMapperList.insert(ColorTokenValueType(OUString("cadetblue"), Color( 95, 158, 160)));
661 aColorTokenMapperList.insert(ColorTokenValueType(OUString("chartreuse"), Color(127, 255, 0)));
662 aColorTokenMapperList.insert(ColorTokenValueType(OUString("chocolate"), Color(210, 105, 30)));
663 aColorTokenMapperList.insert(ColorTokenValueType(OUString("coral"), Color(255, 127, 80)));
664 aColorTokenMapperList.insert(ColorTokenValueType(OUString("cornflowerblue"), Color(100, 149, 237)));
665 aColorTokenMapperList.insert(ColorTokenValueType(OUString("cornsilk"), Color(255, 248, 220)));
666 aColorTokenMapperList.insert(ColorTokenValueType(OUString("crimson"), Color(220, 20, 60)));
667 aColorTokenMapperList.insert(ColorTokenValueType(OUString("cyan"), Color( 0, 255, 255)));
668 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkblue"), Color( 0, 0, 139)));
669 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkcyan"), Color( 0, 139, 139)));
670 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkgoldenrod"), Color(184, 134, 11)));
671 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkgray"), Color(169, 169, 169)));
672 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkgreen"), Color( 0, 100, 0)));
673 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkgrey"), Color(169, 169, 169)));
674 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkkhaki"), Color(189, 183, 107)));
675 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkmagenta"), Color(139, 0, 139)));
676 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkolivegreen"), Color( 85, 107, 47)));
677 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkorange"), Color(255, 140, 0)));
678 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkorchid"), Color(153, 50, 204)));
679 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkred"), Color(139, 0, 0)));
680 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darksalmon"), Color(233, 150, 122)));
681 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkseagreen"), Color(143, 188, 143)));
682 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkslateblue"), Color( 72, 61, 139)));
683 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkslategray"), Color( 47, 79, 79)));
684 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkslategrey"), Color( 47, 79, 79)));
685 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkturquoise"), Color( 0, 206, 209)));
686 aColorTokenMapperList.insert(ColorTokenValueType(OUString("darkviolet"), Color(148, 0, 211)));
687 aColorTokenMapperList.insert(ColorTokenValueType(OUString("deeppink"), Color(255, 20, 147)));
688 aColorTokenMapperList.insert(ColorTokenValueType(OUString("deepskyblue"), Color( 0, 191, 255)));
689 aColorTokenMapperList.insert(ColorTokenValueType(OUString("dimgray"), Color(105, 105, 105)));
690 aColorTokenMapperList.insert(ColorTokenValueType(OUString("dimgrey"), Color(105, 105, 105)));
691 aColorTokenMapperList.insert(ColorTokenValueType(OUString("dodgerblue"), Color( 30, 144, 255)));
692 aColorTokenMapperList.insert(ColorTokenValueType(OUString("firebrick"), Color(178, 34, 34)));
693 aColorTokenMapperList.insert(ColorTokenValueType(OUString("floralwhite"), Color(255, 250, 240)));
694 aColorTokenMapperList.insert(ColorTokenValueType(OUString("forestgreen"), Color( 34, 139, 34)));
695 aColorTokenMapperList.insert(ColorTokenValueType(OUString("fuchsia"), Color(255, 0, 255)));
696 aColorTokenMapperList.insert(ColorTokenValueType(OUString("gainsboro"), Color(220, 220, 220)));
697 aColorTokenMapperList.insert(ColorTokenValueType(OUString("ghostwhite"), Color(248, 248, 255)));
698 aColorTokenMapperList.insert(ColorTokenValueType(OUString("gold"), Color(255, 215, 0)));
699 aColorTokenMapperList.insert(ColorTokenValueType(OUString("goldenrod"), Color(218, 165, 32)));
700 aColorTokenMapperList.insert(ColorTokenValueType(OUString("gray"), Color(128, 128, 128)));
701 aColorTokenMapperList.insert(ColorTokenValueType(OUString("grey"), Color(128, 128, 128)));
702 aColorTokenMapperList.insert(ColorTokenValueType(OUString("green"), Color(0, 128, 0)));
703 aColorTokenMapperList.insert(ColorTokenValueType(OUString("greenyellow"), Color(173, 255, 47)));
704 aColorTokenMapperList.insert(ColorTokenValueType(OUString("honeydew"), Color(240, 255, 240)));
705 aColorTokenMapperList.insert(ColorTokenValueType(OUString("hotpink"), Color(255, 105, 180)));
706 aColorTokenMapperList.insert(ColorTokenValueType(OUString("indianred"), Color(205, 92, 92)));
707 aColorTokenMapperList.insert(ColorTokenValueType(OUString("indigo"), Color( 75, 0, 130)));
708 aColorTokenMapperList.insert(ColorTokenValueType(OUString("ivory"), Color(255, 255, 240)));
709 aColorTokenMapperList.insert(ColorTokenValueType(OUString("khaki"), Color(240, 230, 140)));
710 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lavender"), Color(230, 230, 250)));
711 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lavenderblush"), Color(255, 240, 245)));
712 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lawngreen"), Color(124, 252, 0)));
713 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lemonchiffon"), Color(255, 250, 205)));
714 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightblue"), Color(173, 216, 230)));
715 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightcoral"), Color(240, 128, 128)));
716 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightcyan"), Color(224, 255, 255)));
717 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightgoldenrodyellow"), Color(250, 250, 210)));
718 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightgray"), Color(211, 211, 211)));
719 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightgreen"), Color(144, 238, 144)));
720 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightgrey"), Color(211, 211, 211)));
721 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightpink"), Color(255, 182, 193)));
722 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightsalmon"), Color(255, 160, 122)));
723 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightseagreen"), Color( 32, 178, 170)));
724 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightskyblue"), Color(135, 206, 250)));
725 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightslategray"), Color(119, 136, 153)));
726 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightslategrey"), Color(119, 136, 153)));
727 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightsteelblue"), Color(176, 196, 222)));
728 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lightyellow"), Color(255, 255, 224)));
729 aColorTokenMapperList.insert(ColorTokenValueType(OUString("lime"), Color( 0, 255, 0)));
730 aColorTokenMapperList.insert(ColorTokenValueType(OUString("limegreen"), Color( 50, 205, 50)));
731 aColorTokenMapperList.insert(ColorTokenValueType(OUString("linen"), Color(250, 240, 230)));
732 aColorTokenMapperList.insert(ColorTokenValueType(OUString("magenta"), Color(255, 0, 255)));
733 aColorTokenMapperList.insert(ColorTokenValueType(OUString("maroon"), Color(128, 0, 0)));
734 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumaquamarine"), Color(102, 205, 170)));
735 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumblue"), Color( 0, 0, 205)));
736 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumorchid"), Color(186, 85, 211)));
737 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumpurple"), Color(147, 112, 219)));
738 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumseagreen"), Color( 60, 179, 113)));
739 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumslateblue"), Color(123, 104, 238)));
740 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumspringgreen"), Color( 0, 250, 154)));
741 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumturquoise"), Color( 72, 209, 204)));
742 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mediumvioletred"), Color(199, 21, 133)));
743 aColorTokenMapperList.insert(ColorTokenValueType(OUString("midnightblue"), Color( 25, 25, 112)));
744 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mintcream"), Color(245, 255, 250)));
745 aColorTokenMapperList.insert(ColorTokenValueType(OUString("mistyrose"), Color(255, 228, 225)));
746 aColorTokenMapperList.insert(ColorTokenValueType(OUString("moccasin"), Color(255, 228, 181)));
747 aColorTokenMapperList.insert(ColorTokenValueType(OUString("navajowhite"), Color(255, 222, 173)));
748 aColorTokenMapperList.insert(ColorTokenValueType(OUString("navy"), Color( 0, 0, 128)));
749 aColorTokenMapperList.insert(ColorTokenValueType(OUString("oldlace"), Color(253, 245, 230)));
750 aColorTokenMapperList.insert(ColorTokenValueType(OUString("olive"), Color(128, 128, 0)));
751 aColorTokenMapperList.insert(ColorTokenValueType(OUString("olivedrab"), Color(107, 142, 35)));
752 aColorTokenMapperList.insert(ColorTokenValueType(OUString("orange"), Color(255, 165, 0)));
753 aColorTokenMapperList.insert(ColorTokenValueType(OUString("orangered"), Color(255, 69, 0)));
754 aColorTokenMapperList.insert(ColorTokenValueType(OUString("orchid"), Color(218, 112, 214)));
755 aColorTokenMapperList.insert(ColorTokenValueType(OUString("palegoldenrod"), Color(238, 232, 170)));
756 aColorTokenMapperList.insert(ColorTokenValueType(OUString("palegreen"), Color(152, 251, 152)));
757 aColorTokenMapperList.insert(ColorTokenValueType(OUString("paleturquoise"), Color(175, 238, 238)));
758 aColorTokenMapperList.insert(ColorTokenValueType(OUString("palevioletred"), Color(219, 112, 147)));
759 aColorTokenMapperList.insert(ColorTokenValueType(OUString("papayawhip"), Color(255, 239, 213)));
760 aColorTokenMapperList.insert(ColorTokenValueType(OUString("peachpuff"), Color(255, 218, 185)));
761 aColorTokenMapperList.insert(ColorTokenValueType(OUString("peru"), Color(205, 133, 63)));
762 aColorTokenMapperList.insert(ColorTokenValueType(OUString("pink"), Color(255, 192, 203)));
763 aColorTokenMapperList.insert(ColorTokenValueType(OUString("plum"), Color(221, 160, 221)));
764 aColorTokenMapperList.insert(ColorTokenValueType(OUString("powderblue"), Color(176, 224, 230)));
765 aColorTokenMapperList.insert(ColorTokenValueType(OUString("purple"), Color(128, 0, 128)));
766 aColorTokenMapperList.insert(ColorTokenValueType(OUString("red"), Color(255, 0, 0)));
767 aColorTokenMapperList.insert(ColorTokenValueType(OUString("rosybrown"), Color(188, 143, 143)));
768 aColorTokenMapperList.insert(ColorTokenValueType(OUString("royalblue"), Color( 65, 105, 225)));
769 aColorTokenMapperList.insert(ColorTokenValueType(OUString("saddlebrown"), Color(139, 69, 19)));
770 aColorTokenMapperList.insert(ColorTokenValueType(OUString("salmon"), Color(250, 128, 114)));
771 aColorTokenMapperList.insert(ColorTokenValueType(OUString("sandybrown"), Color(244, 164, 96)));
772 aColorTokenMapperList.insert(ColorTokenValueType(OUString("seagreen"), Color( 46, 139, 87)));
773 aColorTokenMapperList.insert(ColorTokenValueType(OUString("seashell"), Color(255, 245, 238)));
774 aColorTokenMapperList.insert(ColorTokenValueType(OUString("sienna"), Color(160, 82, 45)));
775 aColorTokenMapperList.insert(ColorTokenValueType(OUString("silver"), Color(192, 192, 192)));
776 aColorTokenMapperList.insert(ColorTokenValueType(OUString("skyblue"), Color(135, 206, 235)));
777 aColorTokenMapperList.insert(ColorTokenValueType(OUString("slateblue"), Color(106, 90, 205)));
778 aColorTokenMapperList.insert(ColorTokenValueType(OUString("slategray"), Color(112, 128, 144)));
779 aColorTokenMapperList.insert(ColorTokenValueType(OUString("slategrey"), Color(112, 128, 144)));
780 aColorTokenMapperList.insert(ColorTokenValueType(OUString("snow"), Color(255, 250, 250)));
781 aColorTokenMapperList.insert(ColorTokenValueType(OUString("springgreen"), Color( 0, 255, 127)));
782 aColorTokenMapperList.insert(ColorTokenValueType(OUString("steelblue"), Color( 70, 130, 180)));
783 aColorTokenMapperList.insert(ColorTokenValueType(OUString("tan"), Color(210, 180, 140)));
784 aColorTokenMapperList.insert(ColorTokenValueType(OUString("teal"), Color( 0, 128, 128)));
785 aColorTokenMapperList.insert(ColorTokenValueType(OUString("thistle"), Color(216, 191, 216)));
786 aColorTokenMapperList.insert(ColorTokenValueType(OUString("tomato"), Color(255, 99, 71)));
787 aColorTokenMapperList.insert(ColorTokenValueType(OUString("turquoise"), Color( 64, 224, 208)));
788 aColorTokenMapperList.insert(ColorTokenValueType(OUString("violet"), Color(238, 130, 238)));
789 aColorTokenMapperList.insert(ColorTokenValueType(OUString("wheat"), Color(245, 222, 179)));
790 aColorTokenMapperList.insert(ColorTokenValueType(OUString("white"), Color(255, 255, 255)));
791 aColorTokenMapperList.insert(ColorTokenValueType(OUString("whitesmoke"), Color(245, 245, 245)));
792 aColorTokenMapperList.insert(ColorTokenValueType(OUString("yellow"), Color(255, 255, 0)));
793 aColorTokenMapperList.insert(ColorTokenValueType(OUString("yellowgreen"), Color(154, 205, 50)));
796 ColorTokenMapper::const_iterator aResult(aColorTokenMapperList.find(rName));
798 if(bCaseIndependent && aResult == aColorTokenMapperList.end())
800 // also try case independent match (e.g. for Css styles)
801 aResult = aColorTokenMapperList.find(rName.toAsciiLowerCase());
804 if(aResult == aColorTokenMapperList.end())
806 return false;
808 else
810 rColor = aResult->second.getBColor();
811 return true;
815 bool read_color(const OUString& rCandidate, basegfx::BColor& rColor, bool bCaseIndependent)
817 const sal_Int32 nLen(rCandidate.getLength());
819 if(nLen)
821 const sal_Unicode aChar(rCandidate[0]);
822 const double fFactor(1.0 / 255.0);
824 if(aChar == '#')
826 // hex definition
827 OUStringBuffer aNum;
828 sal_Int32 nPos(1);
830 copyHex(rCandidate, nPos, aNum, nLen);
831 const sal_Int32 nLength(aNum.getLength());
833 if(3 == nLength)
835 const sal_Int32 nR(read_hex(aNum[0]));
836 const sal_Int32 nG(read_hex(aNum[1]));
837 const sal_Int32 nB(read_hex(aNum[2]));
839 rColor.setRed((nR | (nR << 4)) * fFactor);
840 rColor.setGreen((nG | (nG << 4)) * fFactor);
841 rColor.setBlue((nB | (nB << 4)) * fFactor);
843 return true;
845 else if(6 == nLength)
847 const sal_Int32 nR1(read_hex(aNum[0]));
848 const sal_Int32 nR2(read_hex(aNum[1]));
849 const sal_Int32 nG1(read_hex(aNum[2]));
850 const sal_Int32 nG2(read_hex(aNum[3]));
851 const sal_Int32 nB1(read_hex(aNum[4]));
852 const sal_Int32 nB2(read_hex(aNum[5]));
854 rColor.setRed((nR2 | (nR1 << 4)) * fFactor);
855 rColor.setGreen((nG2 | (nG1 << 4)) * fFactor);
856 rColor.setBlue((nB2 | (nB1 << 4)) * fFactor);
858 return true;
861 else
863 static const char aStrRgb[] = "rgb";
865 if(rCandidate.matchIgnoreAsciiCase(aStrRgb, 0))
867 // rgb definition
868 sal_Int32 nPos(strlen(aStrRgb));
869 skip_char(rCandidate, ' ', '(', nPos, nLen);
870 double fR(0.0);
872 if(readNumber(rCandidate, nPos, fR, nLen))
874 skip_char(rCandidate, ' ', nPos, nLen);
876 if(nPos < nLen)
878 const sal_Unicode aPercentChar(rCandidate[nPos]);
879 const bool bIsPercent('%' == aPercentChar);
880 double fG(0.0);
882 if(bIsPercent)
884 skip_char(rCandidate, '%', nPos, nLen);
887 skip_char(rCandidate, ' ', ',', nPos, nLen);
889 if(readNumber(rCandidate, nPos, fG, nLen))
891 double fB(0.0);
893 if(bIsPercent)
895 skip_char(rCandidate, '%', nPos, nLen);
898 skip_char(rCandidate, ' ', ',', nPos, nLen);
900 if(readNumber(rCandidate, nPos, fB, nLen))
902 const double fFac(bIsPercent ? 0.01 : fFactor);
904 rColor.setRed(fR * fFac);
905 rColor.setGreen(fG * fFac);
906 rColor.setBlue(fB * fFac);
908 if(bIsPercent)
910 skip_char(rCandidate, '%', nPos, nLen);
913 skip_char(rCandidate, ' ', ')', nPos, nLen);
914 return true;
920 else
922 // color keyword
923 if(match_colorKeyword(rColor, rCandidate, bCaseIndependent))
925 return true;
931 return false;
934 basegfx::B2DRange readViewBox(const OUString& rCandidate, InfoProvider& rInfoProvider)
936 const sal_Int32 nLen(rCandidate.getLength());
938 if(nLen)
940 sal_Int32 nPos(0);
941 SvgNumber aMinX;
942 skip_char(rCandidate, ' ', ',', nPos, nLen);
944 if(readNumberAndUnit(rCandidate, nPos, aMinX, nLen))
946 SvgNumber aMinY;
947 skip_char(rCandidate, ' ', ',', nPos, nLen);
949 if(readNumberAndUnit(rCandidate, nPos, aMinY, nLen))
951 SvgNumber aWidth;
952 skip_char(rCandidate, ' ', ',', nPos, nLen);
954 if(readNumberAndUnit(rCandidate, nPos, aWidth, nLen))
956 SvgNumber aHeight;
957 skip_char(rCandidate, ' ', ',', nPos, nLen);
959 if(readNumberAndUnit(rCandidate, nPos, aHeight, nLen))
961 double fX(aMinX.solve(rInfoProvider, xcoordinate));
962 double fY(aMinY.solve(rInfoProvider, ycoordinate));
963 double fW(aWidth.solve(rInfoProvider,xcoordinate));
964 double fH(aHeight.solve(rInfoProvider,ycoordinate));
965 return basegfx::B2DRange(fX,fY,fX+fW,fY+fH);
972 return basegfx::B2DRange();
975 basegfx::B2DHomMatrix readTransform(const OUString& rCandidate, InfoProvider& rInfoProvider)
977 basegfx::B2DHomMatrix aMatrix;
978 const sal_Int32 nLen(rCandidate.getLength());
980 if(nLen)
982 sal_Int32 nPos(0);
983 skip_char(rCandidate, ' ', ',', nPos, nLen);
985 while(nPos < nLen)
987 const sal_Unicode aChar(rCandidate[nPos]);
988 const sal_Int32 nInitPos(nPos);
989 static const char aStrMatrix[] = "matrix";
990 static const char aStrTranslate[] = "translate";
991 static const char aStrScale[] = "scale";
992 static const char aStrRotate[] = "rotate";
993 static const char aStrSkewX[] = "skewX";
994 static const char aStrSkewY[] = "skewY";
996 switch(aChar)
998 case sal_Unicode('m') :
1000 if(rCandidate.match(aStrMatrix, nPos))
1002 // matrix element
1003 nPos += strlen(aStrMatrix);
1004 skip_char(rCandidate, ' ', '(', nPos, nLen);
1005 SvgNumber aVal;
1006 basegfx::B2DHomMatrix aNew;
1008 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1010 aNew.set(0, 0, aVal.solve(rInfoProvider)); // Element A
1011 skip_char(rCandidate, ' ', ',', nPos, nLen);
1013 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1015 aNew.set(1, 0, aVal.solve(rInfoProvider)); // Element B
1016 skip_char(rCandidate, ' ', ',', nPos, nLen);
1018 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1020 aNew.set(0, 1, aVal.solve(rInfoProvider)); // Element C
1021 skip_char(rCandidate, ' ', ',', nPos, nLen);
1023 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1025 aNew.set(1, 1, aVal.solve(rInfoProvider)); // Element D
1026 skip_char(rCandidate, ' ', ',', nPos, nLen);
1028 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1030 aNew.set(0, 2, aVal.solve(rInfoProvider, xcoordinate)); // Element E
1031 skip_char(rCandidate, ' ', ',', nPos, nLen);
1033 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1035 aNew.set(1, 2, aVal.solve(rInfoProvider, ycoordinate)); // Element F
1036 skip_char(rCandidate, ' ', ')', nPos, nLen);
1037 skip_char(rCandidate, ' ', ',', nPos, nLen);
1039 // caution: String is evaluated from left to right, but matrix multiplication
1040 // in SVG is right to left, so put the new transformation before the current
1041 // one by multiplicating from the right side
1042 aMatrix = aMatrix * aNew;
1050 break;
1052 case sal_Unicode('t') :
1054 if(rCandidate.match(aStrTranslate, nPos))
1056 // translate element
1057 nPos += strlen(aStrTranslate);
1058 skip_char(rCandidate, ' ', '(', nPos, nLen);
1059 SvgNumber aTransX;
1061 if(readNumberAndUnit(rCandidate, nPos, aTransX, nLen))
1063 skip_char(rCandidate, ' ', ',', nPos, nLen);
1064 SvgNumber aTransY;
1065 readNumberAndUnit(rCandidate, nPos, aTransY, nLen);
1066 skip_char(rCandidate, ' ', ')', nPos, nLen);
1067 skip_char(rCandidate, ' ', ',', nPos, nLen);
1069 aMatrix = aMatrix * basegfx::tools::createTranslateB2DHomMatrix(
1070 aTransX.solve(rInfoProvider, xcoordinate),
1071 aTransY.solve(rInfoProvider, ycoordinate));
1074 break;
1076 case sal_Unicode('s') :
1078 if(rCandidate.match(aStrScale, nPos))
1080 // scale element
1081 nPos += strlen(aStrScale);
1082 skip_char(rCandidate, ' ', '(', nPos, nLen);
1083 SvgNumber aScaleX;
1085 if(readNumberAndUnit(rCandidate, nPos, aScaleX, nLen))
1087 skip_char(rCandidate, ' ', ',', nPos, nLen);
1088 SvgNumber aScaleY(aScaleX);
1089 readNumberAndUnit(rCandidate, nPos, aScaleY, nLen);
1090 skip_char(rCandidate, ' ', ')', nPos, nLen);
1091 skip_char(rCandidate, ' ', ',', nPos, nLen);
1093 aMatrix = aMatrix * basegfx::tools::createScaleB2DHomMatrix(
1094 aScaleX.solve(rInfoProvider),
1095 aScaleY.solve(rInfoProvider));
1098 else if(rCandidate.match(aStrSkewX, nPos))
1100 // skewx element
1101 nPos += strlen(aStrSkewX);
1102 skip_char(rCandidate, ' ', '(', nPos, nLen);
1103 double fSkewX(0.0);
1105 if(readAngle(rCandidate, nPos, fSkewX, nLen))
1107 skip_char(rCandidate, ' ', ')', nPos, nLen);
1108 skip_char(rCandidate, ' ', ',', nPos, nLen);
1110 aMatrix = aMatrix * basegfx::tools::createShearXB2DHomMatrix(tan(fSkewX));
1113 else if(rCandidate.match(aStrSkewY, nPos))
1115 // skewy element
1116 nPos += strlen(aStrSkewY);
1117 skip_char(rCandidate, ' ', '(', nPos, nLen);
1118 double fSkewY(0.0);
1120 if(readAngle(rCandidate, nPos, fSkewY, nLen))
1122 skip_char(rCandidate, ' ', ')', nPos, nLen);
1123 skip_char(rCandidate, ' ', ',', nPos, nLen);
1125 aMatrix = aMatrix * basegfx::tools::createShearYB2DHomMatrix(tan(fSkewY));
1128 break;
1130 case sal_Unicode('r') :
1132 if(rCandidate.match(aStrRotate, nPos))
1134 // rotate element
1135 nPos += strlen(aStrRotate);
1136 skip_char(rCandidate, ' ', '(', nPos, nLen);
1137 double fAngle(0.0);
1139 if(readAngle(rCandidate, nPos, fAngle, nLen))
1141 skip_char(rCandidate, ' ', ',', nPos, nLen);
1142 SvgNumber aX;
1143 readNumberAndUnit(rCandidate, nPos, aX, nLen);
1144 skip_char(rCandidate, ' ', ',', nPos, nLen);
1145 SvgNumber aY;
1146 readNumberAndUnit(rCandidate, nPos, aY, nLen);
1147 skip_char(rCandidate, ' ', ')', nPos, nLen);
1148 skip_char(rCandidate, ' ', ',', nPos, nLen);
1150 const double fX(aX.isSet() ? aX.solve(rInfoProvider, xcoordinate) : 0.0);
1151 const double fY(aY.isSet() ? aY.solve(rInfoProvider, ycoordinate) : 0.0);
1153 if(!basegfx::fTools::equalZero(fX) || !basegfx::fTools::equalZero(fY))
1155 // rotate around point
1156 aMatrix = aMatrix * basegfx::tools::createRotateAroundPoint(fX, fY, fAngle);
1158 else
1160 // rotate
1161 aMatrix = aMatrix * basegfx::tools::createRotateB2DHomMatrix(fAngle);
1165 break;
1169 if(nInitPos == nPos)
1171 OSL_ENSURE(false, "Could not interpret on current position (!)");
1172 nPos++;
1177 return aMatrix;
1180 bool readSingleNumber(const OUString& rCandidate, SvgNumber& aNum)
1182 const sal_Int32 nLen(rCandidate.getLength());
1183 sal_Int32 nPos(0);
1185 return readNumberAndUnit(rCandidate, nPos, aNum, nLen);
1188 bool readLocalUrl(const OUString& rCandidate, OUString& rURL)
1190 static const char aStrUrl[] = "url";
1192 if(rCandidate.startsWith(aStrUrl))
1194 const sal_Int32 nLen(rCandidate.getLength());
1195 sal_Int32 nPos(strlen(aStrUrl));
1197 skip_char(rCandidate, '(', '#', nPos, nLen);
1198 OUStringBuffer aTokenValue;
1199 copyToLimiter(rCandidate, ')', nPos, aTokenValue, nLen);
1200 rURL = aTokenValue.makeStringAndClear();
1202 return true;
1205 return false;
1208 bool readSvgPaint(const OUString& rCandidate, SvgPaint& rSvgPaint, OUString& rURL, bool bCaseIndependent)
1210 if( !rCandidate.isEmpty() )
1212 basegfx::BColor aColor;
1214 if(read_color(rCandidate, aColor, bCaseIndependent))
1216 rSvgPaint = SvgPaint(aColor, true, true);
1217 return true;
1219 else
1221 if(rCandidate.startsWith("none"))
1223 rSvgPaint = SvgPaint(aColor, true, false, false);
1224 return true;
1226 else if(readLocalUrl(rCandidate, rURL))
1228 /// Url is copied to rURL, but needs to be solved outside this helper
1229 return false;
1231 else if(rCandidate.startsWith("currentColor"))
1233 rSvgPaint = SvgPaint(aColor, true, true, true);
1234 return true;
1239 return false;
1242 bool readSvgNumberVector(const OUString& rCandidate, SvgNumberVector& rSvgNumberVector)
1244 const sal_Int32 nLen(rCandidate.getLength());
1245 rSvgNumberVector.clear();
1247 if(nLen)
1249 sal_Int32 nPos(0);
1250 SvgNumber aNum;
1251 skip_char(rCandidate, ' ', ',', nPos, nLen);
1253 while(readNumberAndUnit(rCandidate, nPos, aNum, nLen))
1255 rSvgNumberVector.push_back(aNum);
1256 skip_char(rCandidate, ' ', ',', nPos, nLen);
1259 return !rSvgNumberVector.empty();
1262 return false;
1265 SvgAspectRatio readSvgAspectRatio(const OUString& rCandidate)
1267 const sal_Int32 nLen(rCandidate.getLength());
1269 if(nLen)
1271 sal_Int32 nPos(0);
1272 SvgAlign aSvgAlign(Align_xMidYMid);
1273 bool bDefer(false);
1274 bool bMeetOrSlice(true);
1275 bool bChanged(false);
1277 while(nPos < nLen)
1279 const sal_Int32 nInitPos(nPos);
1280 skip_char(rCandidate, ' ', nPos, nLen);
1281 OUStringBuffer aTokenName;
1282 copyString(rCandidate, nPos, aTokenName, nLen);
1284 if(!aTokenName.isEmpty())
1286 switch(StrToSVGToken(aTokenName.makeStringAndClear(), false))
1288 case SVGTokenDefer:
1290 bDefer = true;
1291 bChanged = true;
1292 break;
1294 case SVGTokenNone:
1296 aSvgAlign = Align_none;
1297 bChanged = true;
1298 break;
1300 case SVGTokenXMinYMin:
1302 aSvgAlign = Align_xMinYMin;
1303 bChanged = true;
1304 break;
1306 case SVGTokenXMidYMin:
1308 aSvgAlign = Align_xMidYMin;
1309 bChanged = true;
1310 break;
1312 case SVGTokenXMaxYMin:
1314 aSvgAlign = Align_xMaxYMin;
1315 bChanged = true;
1316 break;
1318 case SVGTokenXMinYMid:
1320 aSvgAlign = Align_xMinYMid;
1321 bChanged = true;
1322 break;
1324 case SVGTokenXMidYMid:
1326 aSvgAlign = Align_xMidYMid;
1327 bChanged = true;
1328 break;
1330 case SVGTokenXMaxYMid:
1332 aSvgAlign = Align_xMaxYMid;
1333 bChanged = true;
1334 break;
1336 case SVGTokenXMinYMax:
1338 aSvgAlign = Align_xMinYMax;
1339 bChanged = true;
1340 break;
1342 case SVGTokenXMidYMax:
1344 aSvgAlign = Align_xMidYMax;
1345 bChanged = true;
1346 break;
1348 case SVGTokenXMaxYMax:
1350 aSvgAlign = Align_xMaxYMax;
1351 bChanged = true;
1352 break;
1354 case SVGTokenMeet:
1356 bMeetOrSlice = true;
1357 bChanged = true;
1358 break;
1360 case SVGTokenSlice:
1362 bMeetOrSlice = false;
1363 bChanged = true;
1364 break;
1366 default:
1368 break;
1373 if(nInitPos == nPos)
1375 OSL_ENSURE(false, "Could not interpret on current position (!)");
1376 nPos++;
1380 if(bChanged)
1382 return SvgAspectRatio(aSvgAlign, bDefer, bMeetOrSlice);
1386 return SvgAspectRatio();
1389 bool readSvgStringVector(const OUString& rCandidate, SvgStringVector& rSvgStringVector)
1391 rSvgStringVector.clear();
1392 const sal_Int32 nLen(rCandidate.getLength());
1394 if(nLen)
1396 sal_Int32 nPos(0);
1397 OUStringBuffer aTokenValue;
1398 skip_char(rCandidate, ' ', ',', nPos, nLen);
1400 while(nPos < nLen)
1402 copyToLimiter(rCandidate, ',', nPos, aTokenValue, nLen);
1403 skip_char(rCandidate, ',', ' ', nPos, nLen);
1404 const OUString aString = aTokenValue.makeStringAndClear();
1406 if(!aString.isEmpty())
1408 rSvgStringVector.push_back(aString);
1413 return !rSvgStringVector.empty();
1416 void readImageLink(const OUString& rCandidate, OUString& rXLink, OUString& rUrl, OUString& rMimeType, OUString& rData)
1418 rXLink.clear();
1419 rUrl.clear();
1420 rMimeType.clear();
1421 rData.clear();
1423 if('#' == rCandidate[0])
1425 // local link
1426 rXLink = rCandidate.copy(1);
1428 else
1430 static const char aStrData[] = "data:";
1432 if(rCandidate.match(aStrData, 0))
1434 // embedded data
1435 sal_Int32 nPos(strlen(aStrData));
1436 sal_Int32 nLen(rCandidate.getLength());
1437 OUStringBuffer aBuffer;
1439 // read mime type
1440 skip_char(rCandidate, ' ', nPos, nLen);
1441 copyToLimiter(rCandidate, ';', nPos, aBuffer, nLen);
1442 skip_char(rCandidate, ' ', ';', nPos, nLen);
1443 rMimeType = aBuffer.makeStringAndClear();
1445 if(!rMimeType.isEmpty() && nPos < nLen)
1447 static const char aStrImage[] = "image";
1449 if(rMimeType.match(aStrImage, 0))
1451 // image data
1452 OUString aData(rCandidate.copy(nPos));
1453 static const char aStrBase64[] = "base64";
1455 if(aData.match(aStrBase64, 0))
1457 // base64 encoded
1458 nPos = strlen(aStrBase64);
1459 nLen = aData.getLength();
1461 skip_char(aData, ' ', ',', nPos, nLen);
1463 if(nPos < nLen)
1465 rData = aData.copy(nPos);
1471 else
1473 // Url (path and filename)
1474 rUrl = rCandidate;
1479 OUString convert(const OUString& rCandidate, const sal_Unicode& rPattern, const sal_Unicode& rNew, bool bRemove)
1481 const sal_Int32 nLen(rCandidate.getLength());
1483 if(nLen)
1485 sal_Int32 nPos(0);
1486 OUStringBuffer aBuffer;
1487 bool bChanged(false);
1489 while(nPos < nLen)
1491 const sal_Unicode aChar(rCandidate[nPos]);
1493 if(rPattern == aChar)
1495 bChanged = true;
1497 if(!bRemove)
1499 aBuffer.append(rNew);
1502 else
1504 aBuffer.append(aChar);
1507 nPos++;
1510 if(bChanged)
1512 return aBuffer.makeStringAndClear();
1516 return rCandidate;
1519 // #i125325#
1520 OUString removeBlockComments(const OUString& rCandidate)
1522 const sal_Int32 nLen(rCandidate.getLength());
1524 if(nLen)
1526 sal_Int32 nPos(0);
1527 OUStringBuffer aBuffer;
1528 bool bChanged(false);
1529 sal_Int32 nInsideComment(0);
1530 const sal_Unicode aCommentSlash('/');
1531 const sal_Unicode aCommentStar('*');
1533 while(nPos < nLen)
1535 const sal_Unicode aChar(rCandidate[nPos]);
1536 const bool bStart(aCommentSlash == aChar && nPos + 1 < nLen && aCommentStar == rCandidate[nPos + 1]);
1537 const bool bEnd(aCommentStar == aChar && nPos + 1 < nLen && aCommentSlash == rCandidate[nPos + 1]);
1539 if(bStart)
1541 nPos += 2;
1542 nInsideComment++;
1543 bChanged = true;
1545 else if(bEnd)
1547 nPos += 2;
1548 nInsideComment--;
1550 else
1552 if(!nInsideComment)
1554 aBuffer.append(aChar);
1557 nPos++;
1561 if(bChanged)
1563 return aBuffer.makeStringAndClear();
1567 return rCandidate;
1570 OUString consolidateContiguosSpace(const OUString& rCandidate)
1572 const sal_Int32 nLen(rCandidate.getLength());
1574 if(nLen)
1576 sal_Int32 nPos(0);
1577 OUStringBuffer aBuffer;
1578 bool bInsideSpace(false);
1579 const sal_Unicode aSpace(' ');
1581 while(nPos < nLen)
1583 const sal_Unicode aChar(rCandidate[nPos]);
1585 if(aSpace == aChar)
1587 bInsideSpace = true;
1589 else
1591 if(bInsideSpace)
1593 bInsideSpace = false;
1594 aBuffer.append(aSpace);
1597 aBuffer.append(aChar);
1600 nPos++;
1603 if(bInsideSpace)
1605 aBuffer.append(aSpace);
1608 if(aBuffer.getLength() != nLen)
1610 return aBuffer.makeStringAndClear();
1614 return rCandidate;
1617 OUString whiteSpaceHandlingDefault(const OUString& rCandidate)
1619 const sal_Unicode aNewline('\n');
1620 const sal_Unicode aTab('\t');
1621 const sal_Unicode aSpace(' ');
1623 // remove all newline characters
1624 OUString aRetval(convert(rCandidate, aNewline, aNewline, true));
1626 // convert tab to space
1627 aRetval = convert(aRetval, aTab, aSpace, false);
1629 // strip of all leading and trailing spaces
1630 aRetval = aRetval.trim();
1632 // consolidate contiguos space
1633 aRetval = consolidateContiguosSpace(aRetval);
1635 return aRetval;
1638 OUString whiteSpaceHandlingPreserve(const OUString& rCandidate)
1640 const sal_Unicode aNewline('\n');
1641 const sal_Unicode aTab('\t');
1642 const sal_Unicode aSpace(' ');
1644 // convert newline to space
1645 OUString aRetval(convert(rCandidate, aNewline, aSpace, false));
1647 // convert tab to space
1648 aRetval = convert(rCandidate, aTab, aSpace, false);
1650 return rCandidate;
1653 ::std::vector< double > solveSvgNumberVector(const SvgNumberVector& rInput, const InfoProvider& rInfoProvider, NumberType aNumberType)
1655 ::std::vector< double > aRetval;
1657 if(!rInput.empty())
1659 const double nCount(rInput.size());
1660 aRetval.reserve(nCount);
1662 for(sal_uInt32 a(0); a < nCount; a++)
1664 aRetval.push_back(rInput[a].solve(rInfoProvider, aNumberType));
1668 return aRetval;
1671 } // end of namespace svgreader
1672 } // end of namespace svgio
1674 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */