nss: upgrade to release 3.73
[LibreOffice.git] / svgio / source / svgreader / svgtools.cxx
blob1aadf895b9a2c76f19a4d627418690868f7dc3f2
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 <sal/log.hxx>
22 #include <osl/diagnose.h>
23 #include <tools/color.hxx>
24 #include <rtl/math.hxx>
25 #include <basegfx/matrix/b2dhommatrix.hxx>
26 #include <basegfx/matrix/b2dhommatrixtools.hxx>
27 #include <svgtoken.hxx>
28 #include <unordered_map>
30 namespace svgio::svgreader
32 basegfx::B2DHomMatrix SvgAspectRatio::createLinearMapping(const basegfx::B2DRange& rTarget, const basegfx::B2DRange& rSource)
34 basegfx::B2DHomMatrix aRetval;
35 const double fSWidth(rSource.getWidth());
36 const double fSHeight(rSource.getHeight());
37 const bool bNoSWidth(basegfx::fTools::equalZero(fSWidth));
38 const bool bNoSHeight(basegfx::fTools::equalZero(fSHeight));
40 // transform from source state to unit range
41 aRetval.translate(-rSource.getMinX(), -rSource.getMinY());
42 aRetval.scale(
43 (bNoSWidth ? 1.0 : 1.0 / fSWidth) * rTarget.getWidth(),
44 (bNoSHeight ? 1.0 : 1.0 / fSHeight) * rTarget.getHeight());
46 // transform from unit rage to target range
47 aRetval.translate(rTarget.getMinX(), rTarget.getMinY());
49 return aRetval;
52 basegfx::B2DHomMatrix SvgAspectRatio::createMapping(const basegfx::B2DRange& rTarget, const basegfx::B2DRange& rSource) const
54 // removed !isSet() from below. Due to correct defaults in the constructor an instance
55 // of this class is perfectly useful without being set by any importer
56 if(Align_none == getSvgAlign())
58 // create linear mapping (default)
59 return createLinearMapping(rTarget, rSource);
62 basegfx::B2DHomMatrix aRetval;
64 const double fSWidth(rSource.getWidth());
65 const double fSHeight(rSource.getHeight());
66 const bool bNoSWidth(basegfx::fTools::equalZero(fSWidth));
67 const bool bNoSHeight(basegfx::fTools::equalZero(fSHeight));
68 const double fScaleX((bNoSWidth ? 1.0 : 1.0 / fSWidth) * rTarget.getWidth());
69 const double fScaleY((bNoSHeight ? 1.0 : 1.0 / fSHeight) * rTarget.getHeight());
70 const double fScale(isMeetOrSlice() ? std::min(fScaleX, fScaleY) : std::max(fScaleX, fScaleY));
72 // remove source translation, apply scale
73 aRetval.translate(-rSource.getMinX(), -rSource.getMinY());
74 aRetval.scale(fScale, fScale);
76 // evaluate horizontal alignment
77 const double fNewWidth(fSWidth * fScale);
78 double fTransX(0.0);
80 switch(getSvgAlign())
82 case Align_xMidYMin:
83 case Align_xMidYMid:
84 case Align_xMidYMax:
86 // centerX
87 const double fFreeSpace(rTarget.getWidth() - fNewWidth);
88 fTransX = fFreeSpace * 0.5;
89 break;
91 case Align_xMaxYMin:
92 case Align_xMaxYMid:
93 case Align_xMaxYMax:
95 // Right align
96 const double fFreeSpace(rTarget.getWidth() - fNewWidth);
97 fTransX = fFreeSpace;
98 break;
100 default: break;
103 // evaluate vertical alignment
104 const double fNewHeight(fSHeight * fScale);
105 double fTransY(0.0);
107 switch(getSvgAlign())
109 case Align_xMinYMid:
110 case Align_xMidYMid:
111 case Align_xMaxYMid:
113 // centerY
114 const double fFreeSpace(rTarget.getHeight() - fNewHeight);
115 fTransY = fFreeSpace * 0.5;
116 break;
118 case Align_xMinYMax:
119 case Align_xMidYMax:
120 case Align_xMaxYMax:
122 // Bottom align
123 const double fFreeSpace(rTarget.getHeight() - fNewHeight);
124 fTransY = fFreeSpace;
125 break;
127 default: break;
130 // add target translation
131 aRetval.translate(
132 rTarget.getMinX() + fTransX,
133 rTarget.getMinY() + fTransY);
135 return aRetval;
138 double SvgNumber::solveNonPercentage(const InfoProvider& rInfoProvider) const
140 if(isSet())
142 switch(meUnit)
144 case Unit_em:
146 return mfNumber * rInfoProvider.getCurrentFontSizeInherited();
148 case Unit_ex:
150 return mfNumber * rInfoProvider.getCurrentXHeightInherited() * 0.5;
152 case Unit_px:
154 return mfNumber;
156 case Unit_pt:
157 case Unit_pc:
158 case Unit_cm:
159 case Unit_mm:
160 case Unit_in:
162 double fRetval(mfNumber);
164 switch(meUnit)
166 case Unit_pt: fRetval *= F_SVG_PIXEL_PER_INCH / 72.0; break;
167 case Unit_pc: fRetval *= F_SVG_PIXEL_PER_INCH / 6.0; break;
168 case Unit_cm: fRetval *= F_SVG_PIXEL_PER_INCH / 2.54; break;
169 case Unit_mm: fRetval *= 0.1 * F_SVG_PIXEL_PER_INCH / 2.54; break;
170 case Unit_in: fRetval *= F_SVG_PIXEL_PER_INCH; break;
171 default: break;
174 return fRetval;
176 case Unit_none:
178 SAL_WARN("svgio", "Design error, this case should have been handled in the caller");
179 return mfNumber;
181 default:
183 OSL_ENSURE(false, "Do not use with percentage! ");
184 return 0.0;
189 /// not set
190 OSL_ENSURE(false, "SvgNumber not set (!)");
191 return 0.0;
194 double SvgNumber::solve(const InfoProvider& rInfoProvider, NumberType aNumberType) const
196 if(isSet())
198 switch(meUnit)
200 case Unit_px:
202 return mfNumber;
204 case Unit_pt:
205 case Unit_pc:
206 case Unit_cm:
207 case Unit_mm:
208 case Unit_in:
209 case Unit_em:
210 case Unit_ex:
211 case Unit_none:
213 return solveNonPercentage( rInfoProvider);
215 case Unit_percent:
217 double fRetval(mfNumber * 0.01);
218 basegfx::B2DRange aViewPort = rInfoProvider.getCurrentViewPort();
220 if ( aViewPort.isEmpty() )
222 SAL_WARN("svgio", "Design error, this case should have been handled in the caller");
223 // no viewPort, assume a normal page size (A4)
224 aViewPort = basegfx::B2DRange(
225 0.0,
226 0.0,
227 210.0 * F_SVG_PIXEL_PER_INCH / 2.54,
228 297.0 * F_SVG_PIXEL_PER_INCH / 2.54);
232 if ( !aViewPort.isEmpty() )
234 if(xcoordinate == aNumberType)
236 // it's a x-coordinate, relative to current width (w)
237 fRetval *= aViewPort.getWidth();
239 else if(ycoordinate == aNumberType)
241 // it's a y-coordinate, relative to current height (h)
242 fRetval *= aViewPort.getHeight();
244 else // length
246 // it's a length, relative to sqrt(w*w + h*h)/sqrt(2)
247 const double fCurrentWidth(aViewPort.getWidth());
248 const double fCurrentHeight(aViewPort.getHeight());
249 const double fCurrentLength(
250 sqrt(fCurrentWidth * fCurrentWidth + fCurrentHeight * fCurrentHeight)/sqrt(2.0));
252 fRetval *= fCurrentLength;
256 return fRetval;
258 default:
260 break;
265 /// not set
266 OSL_ENSURE(false, "SvgNumber not set (!)");
267 return 0.0;
270 bool SvgNumber::isPositive() const
272 return basegfx::fTools::moreOrEqual(mfNumber, 0.0);
275 void skip_char(std::u16string_view rCandidate, sal_Unicode nChar, sal_Int32& nPos, const sal_Int32 nLen)
277 while(nPos < nLen && nChar == rCandidate[nPos])
279 nPos++;
283 void skip_char(std::u16string_view rCandidate, sal_Unicode nCharA, sal_Unicode nCharB, sal_Int32& nPos, const sal_Int32 nLen)
285 while(nPos < nLen && (nCharA == rCandidate[nPos] || nCharB == rCandidate[nPos]))
287 nPos++;
291 void copySign(std::u16string_view rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
293 if(nPos < nLen)
295 const sal_Unicode aChar(rCandidate[nPos]);
297 if('+' == aChar || '-' == aChar)
299 rTarget.append(aChar);
300 nPos++;
305 void copyNumber(std::u16string_view rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
307 bool bOnNumber(true);
309 while(bOnNumber && nPos < nLen)
311 const sal_Unicode aChar(rCandidate[nPos]);
313 bOnNumber = ('0' <= aChar && '9' >= aChar) || '.' == aChar;
315 if(bOnNumber)
317 rTarget.append(aChar);
318 nPos++;
323 void copyHex(std::u16string_view rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
325 bool bOnHex(true);
327 while(bOnHex && nPos < nLen)
329 const sal_Unicode aChar(rCandidate[nPos]);
331 bOnHex = ('0' <= aChar && '9' >= aChar)
332 || ('A' <= aChar && 'F' >= aChar)
333 || ('a' <= aChar && 'f' >= aChar);
335 if(bOnHex)
337 rTarget.append(aChar);
338 nPos++;
343 void copyString(std::u16string_view rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
345 bool bOnChar(true);
347 while(bOnChar && nPos < nLen)
349 const sal_Unicode aChar(rCandidate[nPos]);
351 bOnChar = ('a' <= aChar && 'z' >= aChar)
352 || ('A' <= aChar && 'Z' >= aChar)
353 || '-' == aChar;
355 if(bOnChar)
357 rTarget.append(aChar);
358 nPos++;
363 void copyToLimiter(std::u16string_view rCandidate, sal_Unicode nLimiter, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
365 while(nPos < nLen && nLimiter != rCandidate[nPos])
367 rTarget.append(rCandidate[nPos]);
368 nPos++;
372 bool readNumber(std::u16string_view rCandidate, sal_Int32& nPos, double& fNum, const sal_Int32 nLen)
374 if(nPos < nLen)
376 OUStringBuffer aNum;
378 copySign(rCandidate, nPos, aNum, nLen);
379 copyNumber(rCandidate, nPos, aNum, nLen);
381 if(nPos < nLen)
383 const sal_Unicode aChar(rCandidate[nPos]);
385 if('e' == aChar || 'E' == aChar)
387 // try to read exponential number, but be careful. I had
388 // a case where dx="2em" was used, thus the 'e' was consumed
389 // by error. First try if there are numbers after the 'e',
390 // safe current state
391 nPos++;
392 const OUStringBuffer aNum2(aNum);
393 const sal_Int32 nPosAfterE(nPos);
395 aNum.append(aChar);
396 copySign(rCandidate, nPos, aNum, nLen);
397 copyNumber(rCandidate, nPos, aNum, nLen);
399 if(nPosAfterE == nPos)
401 // no number after 'e', go back. Do not
402 // return false, it's still a valid integer number
403 aNum = aNum2;
404 nPos--;
409 if(!aNum.isEmpty())
411 rtl_math_ConversionStatus eStatus;
413 fNum = rtl::math::stringToDouble(
414 aNum.makeStringAndClear(), '.', ',',
415 &eStatus);
417 return eStatus == rtl_math_ConversionStatus_Ok;
421 return false;
424 SvgUnit readUnit(std::u16string_view rCandidate, sal_Int32& nPos, const sal_Int32 nLen)
426 SvgUnit aRetval(Unit_px);
428 if(nPos < nLen)
430 const sal_Unicode aCharA(rCandidate[nPos]);
432 if(nPos + 1 < nLen)
434 const sal_Unicode aCharB(rCandidate[nPos + 1]);
435 bool bTwoCharValid(false);
437 switch(aCharA)
439 case u'e' :
441 if('m' == aCharB)
443 // 'em' Relative to current font size
444 aRetval = Unit_em;
445 bTwoCharValid = true;
447 else if('x' == aCharB)
449 // 'ex' Relative to current font x-height
450 aRetval = Unit_ex;
451 bTwoCharValid = true;
453 break;
455 case u'p' :
457 if('x' == aCharB)
459 // 'px' UserUnit (default)
460 bTwoCharValid = true;
462 else if('t' == aCharB)
464 // 'pt' == 4/3 px
465 aRetval = Unit_pt;
466 bTwoCharValid = true;
468 else if('c' == aCharB)
470 // 'pc' == 16 px
471 aRetval = Unit_pc;
472 bTwoCharValid = true;
474 break;
476 case u'i' :
478 if('n' == aCharB)
480 // 'in' == 96 px, since CSS 2.1
481 aRetval = Unit_in;
482 bTwoCharValid = true;
484 break;
486 case u'c' :
488 if('m' == aCharB)
490 // 'cm' == 37.79527559 px
491 aRetval = Unit_cm;
492 bTwoCharValid = true;
494 break;
496 case u'm' :
498 if('m' == aCharB)
500 // 'mm' == 3.779528 px
501 aRetval = Unit_mm;
502 bTwoCharValid = true;
504 break;
508 if(bTwoCharValid)
510 nPos += 2;
513 else
515 if('%' == aCharA)
517 // percent used, relative to current
518 nPos++;
519 aRetval = Unit_percent;
524 return aRetval;
527 bool readNumberAndUnit(std::u16string_view rCandidate, sal_Int32& nPos, SvgNumber& aNum, const sal_Int32 nLen)
529 double fNum(0.0);
531 if(readNumber(rCandidate, nPos, fNum, nLen))
533 skip_char(rCandidate, ' ', nPos, nLen);
534 aNum = SvgNumber(fNum, readUnit(rCandidate, nPos, nLen));
536 return true;
539 return false;
542 bool readAngle(const OUString& rCandidate, sal_Int32& nPos, double& fAngle, const sal_Int32 nLen)
544 if(readNumber(rCandidate, nPos, fAngle, nLen))
546 skip_char(rCandidate, ' ', nPos, nLen);
548 enum DegreeType
550 deg,
551 grad,
553 } aType(deg); // degrees is default
555 if(nPos < nLen)
557 const sal_Unicode aChar(rCandidate[nPos]);
558 static const char aStrGrad[] = "grad";
559 static const char aStrRad[] = "rad";
561 switch(aChar)
563 case u'g' :
564 case u'G' :
566 if(rCandidate.matchIgnoreAsciiCase(aStrGrad, nPos))
568 // angle in grad
569 nPos += strlen(aStrGrad);
570 aType = grad;
572 break;
574 case u'r' :
575 case u'R' :
577 if(rCandidate.matchIgnoreAsciiCase(aStrRad, nPos))
579 // angle in radians
580 nPos += strlen(aStrRad);
581 aType = rad;
583 break;
588 // convert to radians
589 if(deg == aType)
591 fAngle = basegfx::deg2rad(fAngle);
593 else if(grad == aType)
595 // looks like 100 grad is 90 degrees
596 fAngle *= F_PI / 200.0;
599 return true;
602 return false;
605 sal_Int32 read_hex(sal_Unicode nChar)
607 if(nChar >= '0' && nChar <= '9')
609 return nChar - u'0';
611 else if(nChar >= 'A' && nChar <= 'F')
613 return 10 + sal_Int32(nChar - u'A');
615 else if(nChar >= 'a' && nChar <= 'f')
617 return 10 + sal_Int32(nChar - u'a');
619 else
621 // error
622 return 0;
626 bool match_colorKeyword(basegfx::BColor& rColor, const OUString& rName, bool bCaseIndependent)
628 typedef std::unordered_map< OUString, Color > ColorTokenMapper;
629 typedef std::pair< OUString, Color > ColorTokenValueType;
630 static const ColorTokenMapper aColorTokenMapperList {
631 { ColorTokenValueType(OUString("aliceblue"), Color(240, 248, 255)) },
632 { ColorTokenValueType(OUString("antiquewhite"), Color(250, 235, 215) ) },
633 { ColorTokenValueType(OUString("aqua"), Color( 0, 255, 255) ) },
634 { ColorTokenValueType(OUString("aquamarine"), Color(127, 255, 212) ) },
635 { ColorTokenValueType(OUString("azure"), Color(240, 255, 255) ) },
636 { ColorTokenValueType(OUString("beige"), Color(245, 245, 220) ) },
637 { ColorTokenValueType(OUString("bisque"), Color(255, 228, 196) ) },
638 { ColorTokenValueType(OUString("black"), Color( 0, 0, 0) ) },
639 { ColorTokenValueType(OUString("blanchedalmond"), Color(255, 235, 205) ) },
640 { ColorTokenValueType(OUString("blue"), Color( 0, 0, 255) ) },
641 { ColorTokenValueType(OUString("blueviolet"), Color(138, 43, 226) ) },
642 { ColorTokenValueType(OUString("brown"), Color(165, 42, 42) ) },
643 { ColorTokenValueType(OUString("burlywood"), Color(222, 184, 135) ) },
644 { ColorTokenValueType(OUString("cadetblue"), Color( 95, 158, 160) ) },
645 { ColorTokenValueType(OUString("chartreuse"), Color(127, 255, 0) ) },
646 { ColorTokenValueType(OUString("chocolate"), Color(210, 105, 30) ) },
647 { ColorTokenValueType(OUString("coral"), Color(255, 127, 80) ) },
648 { ColorTokenValueType(OUString("cornflowerblue"), Color(100, 149, 237) ) },
649 { ColorTokenValueType(OUString("cornsilk"), Color(255, 248, 220) ) },
650 { ColorTokenValueType(OUString("crimson"), Color(220, 20, 60) ) },
651 { ColorTokenValueType(OUString("cyan"), Color( 0, 255, 255) ) },
652 { ColorTokenValueType(OUString("darkblue"), Color( 0, 0, 139) ) },
653 { ColorTokenValueType(OUString("darkcyan"), Color( 0, 139, 139) ) },
654 { ColorTokenValueType(OUString("darkgoldenrod"), Color(184, 134, 11) ) },
655 { ColorTokenValueType(OUString("darkgray"), Color(169, 169, 169) ) },
656 { ColorTokenValueType(OUString("darkgreen"), Color( 0, 100, 0) ) },
657 { ColorTokenValueType(OUString("darkgrey"), Color(169, 169, 169) ) },
658 { ColorTokenValueType(OUString("darkkhaki"), Color(189, 183, 107) ) },
659 { ColorTokenValueType(OUString("darkmagenta"), Color(139, 0, 139) ) },
660 { ColorTokenValueType(OUString("darkolivegreen"), Color( 85, 107, 47) ) },
661 { ColorTokenValueType(OUString("darkorange"), Color(255, 140, 0) ) },
662 { ColorTokenValueType(OUString("darkorchid"), Color(153, 50, 204) ) },
663 { ColorTokenValueType(OUString("darkred"), Color(139, 0, 0) ) },
664 { ColorTokenValueType(OUString("darksalmon"), Color(233, 150, 122) ) },
665 { ColorTokenValueType(OUString("darkseagreen"), Color(143, 188, 143) ) },
666 { ColorTokenValueType(OUString("darkslateblue"), Color( 72, 61, 139) ) },
667 { ColorTokenValueType(OUString("darkslategray"), Color( 47, 79, 79) ) },
668 { ColorTokenValueType(OUString("darkslategrey"), Color( 47, 79, 79) ) },
669 { ColorTokenValueType(OUString("darkturquoise"), Color( 0, 206, 209) ) },
670 { ColorTokenValueType(OUString("darkviolet"), Color(148, 0, 211) ) },
671 { ColorTokenValueType(OUString("deeppink"), Color(255, 20, 147) ) },
672 { ColorTokenValueType(OUString("deepskyblue"), Color( 0, 191, 255) ) },
673 { ColorTokenValueType(OUString("dimgray"), Color(105, 105, 105) ) },
674 { ColorTokenValueType(OUString("dimgrey"), Color(105, 105, 105) ) },
675 { ColorTokenValueType(OUString("dodgerblue"), Color( 30, 144, 255) ) },
676 { ColorTokenValueType(OUString("firebrick"), Color(178, 34, 34) ) },
677 { ColorTokenValueType(OUString("floralwhite"), Color(255, 250, 240) ) },
678 { ColorTokenValueType(OUString("forestgreen"), Color( 34, 139, 34) ) },
679 { ColorTokenValueType(OUString("fuchsia"), Color(255, 0, 255) ) },
680 { ColorTokenValueType(OUString("gainsboro"), Color(220, 220, 220) ) },
681 { ColorTokenValueType(OUString("ghostwhite"), Color(248, 248, 255) ) },
682 { ColorTokenValueType(OUString("gold"), Color(255, 215, 0) ) },
683 { ColorTokenValueType(OUString("goldenrod"), Color(218, 165, 32) ) },
684 { ColorTokenValueType(OUString("gray"), Color(128, 128, 128) ) },
685 { ColorTokenValueType(OUString("grey"), Color(128, 128, 128) ) },
686 { ColorTokenValueType(OUString("green"), Color(0, 128, 0) ) },
687 { ColorTokenValueType(OUString("greenyellow"), Color(173, 255, 47) ) },
688 { ColorTokenValueType(OUString("honeydew"), Color(240, 255, 240) ) },
689 { ColorTokenValueType(OUString("hotpink"), Color(255, 105, 180) ) },
690 { ColorTokenValueType(OUString("indianred"), Color(205, 92, 92) ) },
691 { ColorTokenValueType(OUString("indigo"), Color( 75, 0, 130) ) },
692 { ColorTokenValueType(OUString("ivory"), Color(255, 255, 240) ) },
693 { ColorTokenValueType(OUString("khaki"), Color(240, 230, 140) ) },
694 { ColorTokenValueType(OUString("lavender"), Color(230, 230, 250) ) },
695 { ColorTokenValueType(OUString("lavenderblush"), Color(255, 240, 245) ) },
696 { ColorTokenValueType(OUString("lawngreen"), Color(124, 252, 0) ) },
697 { ColorTokenValueType(OUString("lemonchiffon"), Color(255, 250, 205) ) },
698 { ColorTokenValueType(OUString("lightblue"), Color(173, 216, 230) ) },
699 { ColorTokenValueType(OUString("lightcoral"), Color(240, 128, 128) ) },
700 { ColorTokenValueType(OUString("lightcyan"), Color(224, 255, 255) ) },
701 { ColorTokenValueType(OUString("lightgoldenrodyellow"), Color(250, 250, 210) ) },
702 { ColorTokenValueType(OUString("lightgray"), Color(211, 211, 211) ) },
703 { ColorTokenValueType(OUString("lightgreen"), Color(144, 238, 144) ) },
704 { ColorTokenValueType(OUString("lightgrey"), Color(211, 211, 211) ) },
705 { ColorTokenValueType(OUString("lightpink"), Color(255, 182, 193) ) },
706 { ColorTokenValueType(OUString("lightsalmon"), Color(255, 160, 122) ) },
707 { ColorTokenValueType(OUString("lightseagreen"), Color( 32, 178, 170) ) },
708 { ColorTokenValueType(OUString("lightskyblue"), Color(135, 206, 250) ) },
709 { ColorTokenValueType(OUString("lightslategray"), Color(119, 136, 153) ) },
710 { ColorTokenValueType(OUString("lightslategrey"), Color(119, 136, 153) ) },
711 { ColorTokenValueType(OUString("lightsteelblue"), Color(176, 196, 222) ) },
712 { ColorTokenValueType(OUString("lightyellow"), Color(255, 255, 224) ) },
713 { ColorTokenValueType(OUString("lime"), Color( 0, 255, 0) ) },
714 { ColorTokenValueType(OUString("limegreen"), Color( 50, 205, 50) ) },
715 { ColorTokenValueType(OUString("linen"), Color(250, 240, 230) ) },
716 { ColorTokenValueType(OUString("magenta"), Color(255, 0, 255) ) },
717 { ColorTokenValueType(OUString("maroon"), Color(128, 0, 0) ) },
718 { ColorTokenValueType(OUString("mediumaquamarine"), Color(102, 205, 170) ) },
719 { ColorTokenValueType(OUString("mediumblue"), Color( 0, 0, 205) ) },
720 { ColorTokenValueType(OUString("mediumorchid"), Color(186, 85, 211) ) },
721 { ColorTokenValueType(OUString("mediumpurple"), Color(147, 112, 219) ) },
722 { ColorTokenValueType(OUString("mediumseagreen"), Color( 60, 179, 113) ) },
723 { ColorTokenValueType(OUString("mediumslateblue"), Color(123, 104, 238) ) },
724 { ColorTokenValueType(OUString("mediumspringgreen"), Color( 0, 250, 154) ) },
725 { ColorTokenValueType(OUString("mediumturquoise"), Color( 72, 209, 204) ) },
726 { ColorTokenValueType(OUString("mediumvioletred"), Color(199, 21, 133) ) },
727 { ColorTokenValueType(OUString("midnightblue"), Color( 25, 25, 112) ) },
728 { ColorTokenValueType(OUString("mintcream"), Color(245, 255, 250) ) },
729 { ColorTokenValueType(OUString("mistyrose"), Color(255, 228, 225) ) },
730 { ColorTokenValueType(OUString("moccasin"), Color(255, 228, 181) ) },
731 { ColorTokenValueType(OUString("navajowhite"), Color(255, 222, 173) ) },
732 { ColorTokenValueType(OUString("navy"), Color( 0, 0, 128) ) },
733 { ColorTokenValueType(OUString("oldlace"), Color(253, 245, 230) ) },
734 { ColorTokenValueType(OUString("olive"), Color(128, 128, 0) ) },
735 { ColorTokenValueType(OUString("olivedrab"), Color(107, 142, 35) ) },
736 { ColorTokenValueType(OUString("orange"), Color(255, 165, 0) ) },
737 { ColorTokenValueType(OUString("orangered"), Color(255, 69, 0) ) },
738 { ColorTokenValueType(OUString("orchid"), Color(218, 112, 214) ) },
739 { ColorTokenValueType(OUString("palegoldenrod"), Color(238, 232, 170) ) },
740 { ColorTokenValueType(OUString("palegreen"), Color(152, 251, 152) ) },
741 { ColorTokenValueType(OUString("paleturquoise"), Color(175, 238, 238) ) },
742 { ColorTokenValueType(OUString("palevioletred"), Color(219, 112, 147) ) },
743 { ColorTokenValueType(OUString("papayawhip"), Color(255, 239, 213) ) },
744 { ColorTokenValueType(OUString("peachpuff"), Color(255, 218, 185) ) },
745 { ColorTokenValueType(OUString("peru"), Color(205, 133, 63) ) },
746 { ColorTokenValueType(OUString("pink"), Color(255, 192, 203) ) },
747 { ColorTokenValueType(OUString("plum"), Color(221, 160, 221) ) },
748 { ColorTokenValueType(OUString("powderblue"), Color(176, 224, 230) ) },
749 { ColorTokenValueType(OUString("purple"), Color(128, 0, 128) ) },
750 { ColorTokenValueType(OUString("red"), Color(255, 0, 0) ) },
751 { ColorTokenValueType(OUString("rosybrown"), Color(188, 143, 143) ) },
752 { ColorTokenValueType(OUString("royalblue"), Color( 65, 105, 225) ) },
753 { ColorTokenValueType(OUString("saddlebrown"), Color(139, 69, 19) ) },
754 { ColorTokenValueType(OUString("salmon"), Color(250, 128, 114) ) },
755 { ColorTokenValueType(OUString("sandybrown"), Color(244, 164, 96) ) },
756 { ColorTokenValueType(OUString("seagreen"), Color( 46, 139, 87) ) },
757 { ColorTokenValueType(OUString("seashell"), Color(255, 245, 238) ) },
758 { ColorTokenValueType(OUString("sienna"), Color(160, 82, 45) ) },
759 { ColorTokenValueType(OUString("silver"), Color(192, 192, 192) ) },
760 { ColorTokenValueType(OUString("skyblue"), Color(135, 206, 235) ) },
761 { ColorTokenValueType(OUString("slateblue"), Color(106, 90, 205) ) },
762 { ColorTokenValueType(OUString("slategray"), Color(112, 128, 144) ) },
763 { ColorTokenValueType(OUString("slategrey"), Color(112, 128, 144) ) },
764 { ColorTokenValueType(OUString("snow"), Color(255, 250, 250) ) },
765 { ColorTokenValueType(OUString("springgreen"), Color( 0, 255, 127) ) },
766 { ColorTokenValueType(OUString("steelblue"), Color( 70, 130, 180) ) },
767 { ColorTokenValueType(OUString("tan"), Color(210, 180, 140) ) },
768 { ColorTokenValueType(OUString("teal"), Color( 0, 128, 128) ) },
769 { ColorTokenValueType(OUString("thistle"), Color(216, 191, 216) ) },
770 { ColorTokenValueType(OUString("tomato"), Color(255, 99, 71) ) },
771 { ColorTokenValueType(OUString("turquoise"), Color( 64, 224, 208) ) },
772 { ColorTokenValueType(OUString("violet"), Color(238, 130, 238) ) },
773 { ColorTokenValueType(OUString("wheat"), Color(245, 222, 179) ) },
774 { ColorTokenValueType(OUString("white"), Color(255, 255, 255) ) },
775 { ColorTokenValueType(OUString("whitesmoke"), Color(245, 245, 245) ) },
776 { ColorTokenValueType(OUString("yellow"), Color(255, 255, 0) ) },
777 { ColorTokenValueType(OUString("yellowgreen"), Color(154, 205, 50) ) },
780 ColorTokenMapper::const_iterator aResult(aColorTokenMapperList.find(rName));
782 if(bCaseIndependent && aResult == aColorTokenMapperList.end())
784 // also try case independent match (e.g. for Css styles)
785 aResult = aColorTokenMapperList.find(rName.toAsciiLowerCase());
788 if(aResult == aColorTokenMapperList.end())
790 return false;
792 else
794 rColor = aResult->second.getBColor();
795 return true;
799 bool read_color(const OUString& rCandidate, basegfx::BColor& rColor, bool bCaseIndependent, SvgNumber& rOpacity)
801 const sal_Int32 nLen(rCandidate.getLength());
803 if(nLen)
805 const sal_Unicode aChar(rCandidate[0]);
806 const double fFactor(1.0 / 255.0);
808 if(aChar == '#')
810 // hex definition
811 OUStringBuffer aNum;
812 sal_Int32 nPos(1);
814 copyHex(rCandidate, nPos, aNum, nLen);
815 const sal_Int32 nLength(aNum.getLength());
817 if(3 == nLength)
819 const sal_Int32 nR(read_hex(aNum[0]));
820 const sal_Int32 nG(read_hex(aNum[1]));
821 const sal_Int32 nB(read_hex(aNum[2]));
823 rColor.setRed((nR | (nR << 4)) * fFactor);
824 rColor.setGreen((nG | (nG << 4)) * fFactor);
825 rColor.setBlue((nB | (nB << 4)) * fFactor);
827 return true;
829 else if(6 == nLength)
831 const sal_Int32 nR1(read_hex(aNum[0]));
832 const sal_Int32 nR2(read_hex(aNum[1]));
833 const sal_Int32 nG1(read_hex(aNum[2]));
834 const sal_Int32 nG2(read_hex(aNum[3]));
835 const sal_Int32 nB1(read_hex(aNum[4]));
836 const sal_Int32 nB2(read_hex(aNum[5]));
838 rColor.setRed((nR2 | (nR1 << 4)) * fFactor);
839 rColor.setGreen((nG2 | (nG1 << 4)) * fFactor);
840 rColor.setBlue((nB2 | (nB1 << 4)) * fFactor);
842 return true;
845 else
847 static const char aStrRgb[] = "rgb";
849 if(rCandidate.matchIgnoreAsciiCase(aStrRgb, 0))
851 // rgb/rgba definition
852 sal_Int32 nPos(strlen(aStrRgb));
853 bool bIsRGBA = false;
855 if('a' == rCandidate[nPos])
857 //Delete the 'a' from 'rbga'
858 skip_char(rCandidate, 'a', nPos, nPos + 1);
859 bIsRGBA = true;
862 skip_char(rCandidate, ' ', '(', nPos, nLen);
863 double fR(0.0);
865 if(readNumber(rCandidate, nPos, fR, nLen))
867 skip_char(rCandidate, ' ', nPos, nLen);
869 if(nPos < nLen)
871 const sal_Unicode aPercentChar(rCandidate[nPos]);
872 const bool bIsPercent('%' == aPercentChar);
873 double fG(0.0);
875 if(bIsPercent)
877 skip_char(rCandidate, '%', nPos, nLen);
880 skip_char(rCandidate, ' ', ',', nPos, nLen);
882 if(readNumber(rCandidate, nPos, fG, nLen))
884 double fB(0.0);
886 if(bIsPercent)
888 skip_char(rCandidate, '%', nPos, nLen);
891 skip_char(rCandidate, ' ', ',', nPos, nLen);
893 if(readNumber(rCandidate, nPos, fB, nLen))
895 double fA(1.0);
897 if(bIsPercent)
899 skip_char(rCandidate, '%', nPos, nLen);
902 skip_char(rCandidate, ' ', ',', nPos, nLen);
904 if(readNumber(rCandidate, nPos, fA, nLen))
906 if(bIsRGBA)
908 const double fFac(bIsPercent ? 0.01 : 1);
909 rOpacity = SvgNumber(fA * fFac);
911 if(bIsPercent)
913 skip_char(rCandidate, '%', nPos, nLen);
916 else
918 return false;
922 const double fFac(bIsPercent ? 0.01 : fFactor);
924 rColor.setRed(fR * fFac);
925 rColor.setGreen(fG * fFac);
926 rColor.setBlue(fB * fFac);
928 skip_char(rCandidate, ' ', ')', nPos, nLen);
929 return true;
935 else
937 // color keyword
938 if(match_colorKeyword(rColor, rCandidate, bCaseIndependent))
940 return true;
946 return false;
949 basegfx::B2DRange readViewBox(const OUString& rCandidate, InfoProvider const & rInfoProvider)
951 const sal_Int32 nLen(rCandidate.getLength());
953 if(nLen)
955 sal_Int32 nPos(0);
956 SvgNumber aMinX;
957 skip_char(rCandidate, ' ', ',', nPos, nLen);
959 if(readNumberAndUnit(rCandidate, nPos, aMinX, nLen))
961 SvgNumber aMinY;
962 skip_char(rCandidate, ' ', ',', nPos, nLen);
964 if(readNumberAndUnit(rCandidate, nPos, aMinY, nLen))
966 SvgNumber aWidth;
967 skip_char(rCandidate, ' ', ',', nPos, nLen);
969 if(readNumberAndUnit(rCandidate, nPos, aWidth, nLen))
971 SvgNumber aHeight;
972 skip_char(rCandidate, ' ', ',', nPos, nLen);
974 if(readNumberAndUnit(rCandidate, nPos, aHeight, nLen))
976 double fX(aMinX.solve(rInfoProvider, xcoordinate));
977 double fY(aMinY.solve(rInfoProvider, ycoordinate));
978 double fW(aWidth.solve(rInfoProvider,xcoordinate));
979 double fH(aHeight.solve(rInfoProvider,ycoordinate));
980 return basegfx::B2DRange(fX,fY,fX+fW,fY+fH);
987 return basegfx::B2DRange();
990 basegfx::B2DHomMatrix readTransform(const OUString& rCandidate, InfoProvider const & rInfoProvider)
992 basegfx::B2DHomMatrix aMatrix;
993 const sal_Int32 nLen(rCandidate.getLength());
995 if(nLen)
997 sal_Int32 nPos(0);
998 skip_char(rCandidate, ' ', ',', nPos, nLen);
1000 while(nPos < nLen)
1002 const sal_Unicode aChar(rCandidate[nPos]);
1003 const sal_Int32 nInitPos(nPos);
1004 static const char aStrMatrix[] = "matrix";
1005 static const char aStrTranslate[] = "translate";
1006 static const char aStrScale[] = "scale";
1007 static const char aStrRotate[] = "rotate";
1008 static const char aStrSkewX[] = "skewX";
1009 static const char aStrSkewY[] = "skewY";
1011 switch(aChar)
1013 case u'm' :
1015 if(rCandidate.match(aStrMatrix, nPos))
1017 // matrix element
1018 nPos += strlen(aStrMatrix);
1019 skip_char(rCandidate, ' ', '(', nPos, nLen);
1020 SvgNumber aVal;
1021 basegfx::B2DHomMatrix aNew;
1023 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1025 aNew.set(0, 0, aVal.solve(rInfoProvider)); // Element A
1026 skip_char(rCandidate, ' ', ',', nPos, nLen);
1028 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1030 aNew.set(1, 0, aVal.solve(rInfoProvider)); // Element B
1031 skip_char(rCandidate, ' ', ',', nPos, nLen);
1033 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1035 aNew.set(0, 1, aVal.solve(rInfoProvider)); // Element C
1036 skip_char(rCandidate, ' ', ',', nPos, nLen);
1038 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1040 aNew.set(1, 1, aVal.solve(rInfoProvider)); // Element D
1041 skip_char(rCandidate, ' ', ',', nPos, nLen);
1043 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1045 aNew.set(0, 2, aVal.solve(rInfoProvider, xcoordinate)); // Element E
1046 skip_char(rCandidate, ' ', ',', nPos, nLen);
1048 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
1050 aNew.set(1, 2, aVal.solve(rInfoProvider, ycoordinate)); // Element F
1051 skip_char(rCandidate, ' ', ')', nPos, nLen);
1052 skip_char(rCandidate, ' ', ',', nPos, nLen);
1054 // caution: String is evaluated from left to right, but matrix multiplication
1055 // in SVG is right to left, so put the new transformation before the current
1056 // one by multiplicating from the right side
1057 aMatrix = aMatrix * aNew;
1065 break;
1067 case u't' :
1069 if(rCandidate.match(aStrTranslate, nPos))
1071 // translate element
1072 nPos += strlen(aStrTranslate);
1073 skip_char(rCandidate, ' ', '(', nPos, nLen);
1074 SvgNumber aTransX;
1076 if(readNumberAndUnit(rCandidate, nPos, aTransX, nLen))
1078 skip_char(rCandidate, ' ', ',', nPos, nLen);
1079 SvgNumber aTransY;
1080 readNumberAndUnit(rCandidate, nPos, aTransY, nLen);
1081 skip_char(rCandidate, ' ', ')', nPos, nLen);
1082 skip_char(rCandidate, ' ', ',', nPos, nLen);
1084 aMatrix = aMatrix * basegfx::utils::createTranslateB2DHomMatrix(
1085 aTransX.solve(rInfoProvider, xcoordinate),
1086 aTransY.solve(rInfoProvider, ycoordinate));
1089 break;
1091 case u's' :
1093 if(rCandidate.match(aStrScale, nPos))
1095 // scale element
1096 nPos += strlen(aStrScale);
1097 skip_char(rCandidate, ' ', '(', nPos, nLen);
1098 SvgNumber aScaleX;
1100 if(readNumberAndUnit(rCandidate, nPos, aScaleX, nLen))
1102 skip_char(rCandidate, ' ', ',', nPos, nLen);
1103 SvgNumber aScaleY(aScaleX);
1104 readNumberAndUnit(rCandidate, nPos, aScaleY, nLen);
1105 skip_char(rCandidate, ' ', ')', nPos, nLen);
1106 skip_char(rCandidate, ' ', ',', nPos, nLen);
1108 aMatrix = aMatrix * basegfx::utils::createScaleB2DHomMatrix(
1109 aScaleX.solve(rInfoProvider),
1110 aScaleY.solve(rInfoProvider));
1113 else if(rCandidate.match(aStrSkewX, nPos))
1115 // skewx element
1116 nPos += strlen(aStrSkewX);
1117 skip_char(rCandidate, ' ', '(', nPos, nLen);
1118 double fSkewX(0.0);
1120 if(readAngle(rCandidate, nPos, fSkewX, nLen))
1122 skip_char(rCandidate, ' ', ')', nPos, nLen);
1123 skip_char(rCandidate, ' ', ',', nPos, nLen);
1125 aMatrix = aMatrix * basegfx::utils::createShearXB2DHomMatrix(tan(fSkewX));
1128 else if(rCandidate.match(aStrSkewY, nPos))
1130 // skewy element
1131 nPos += strlen(aStrSkewY);
1132 skip_char(rCandidate, ' ', '(', nPos, nLen);
1133 double fSkewY(0.0);
1135 if(readAngle(rCandidate, nPos, fSkewY, nLen))
1137 skip_char(rCandidate, ' ', ')', nPos, nLen);
1138 skip_char(rCandidate, ' ', ',', nPos, nLen);
1140 aMatrix = aMatrix * basegfx::utils::createShearYB2DHomMatrix(tan(fSkewY));
1143 break;
1145 case u'r' :
1147 if(rCandidate.match(aStrRotate, nPos))
1149 // rotate element
1150 nPos += strlen(aStrRotate);
1151 skip_char(rCandidate, ' ', '(', nPos, nLen);
1152 double fAngle(0.0);
1154 if(readAngle(rCandidate, nPos, fAngle, nLen))
1156 skip_char(rCandidate, ' ', ',', nPos, nLen);
1157 SvgNumber aX;
1158 readNumberAndUnit(rCandidate, nPos, aX, nLen);
1159 skip_char(rCandidate, ' ', ',', nPos, nLen);
1160 SvgNumber aY;
1161 readNumberAndUnit(rCandidate, nPos, aY, nLen);
1162 skip_char(rCandidate, ' ', ')', nPos, nLen);
1163 skip_char(rCandidate, ' ', ',', nPos, nLen);
1165 const double fX(aX.isSet() ? aX.solve(rInfoProvider, xcoordinate) : 0.0);
1166 const double fY(aY.isSet() ? aY.solve(rInfoProvider, ycoordinate) : 0.0);
1168 if(!basegfx::fTools::equalZero(fX) || !basegfx::fTools::equalZero(fY))
1170 // rotate around point
1171 aMatrix = aMatrix * basegfx::utils::createRotateAroundPoint(fX, fY, fAngle);
1173 else
1175 // rotate
1176 aMatrix = aMatrix * basegfx::utils::createRotateB2DHomMatrix(fAngle);
1180 break;
1184 if(nInitPos == nPos)
1186 OSL_ENSURE(false, "Could not interpret on current position (!)");
1187 nPos++;
1192 return aMatrix;
1195 bool readSingleNumber(const OUString& rCandidate, SvgNumber& aNum)
1197 const sal_Int32 nLen(rCandidate.getLength());
1198 sal_Int32 nPos(0);
1200 return readNumberAndUnit(rCandidate, nPos, aNum, nLen);
1203 bool readLocalUrl(const OUString& rCandidate, OUString& rURL)
1205 static const char aStrUrl[] = "url";
1207 if(rCandidate.startsWith(aStrUrl))
1209 const sal_Int32 nLen(rCandidate.getLength());
1210 sal_Int32 nPos(strlen(aStrUrl));
1212 skip_char(rCandidate, '(', '#', nPos, nLen);
1213 OUStringBuffer aTokenValue;
1214 copyToLimiter(rCandidate, ')', nPos, aTokenValue, nLen);
1215 rURL = aTokenValue.makeStringAndClear();
1217 return true;
1220 return false;
1223 bool readSvgPaint(const OUString& rCandidate, SvgPaint& rSvgPaint,
1224 OUString& rURL, bool bCaseIndependent, SvgNumber& rOpacity)
1226 if( !rCandidate.isEmpty() )
1228 basegfx::BColor aColor;
1230 if(read_color(rCandidate, aColor, bCaseIndependent, rOpacity))
1232 rSvgPaint = SvgPaint(aColor, true, true);
1233 return true;
1235 else
1237 if(rCandidate.startsWith("none"))
1239 rSvgPaint = SvgPaint(aColor, true, false, false);
1240 return true;
1242 else if(readLocalUrl(rCandidate, rURL))
1244 /// Url is copied to rURL, but needs to be solved outside this helper
1245 return false;
1247 else if(rCandidate.startsWith("currentColor"))
1249 rSvgPaint = SvgPaint(aColor, true, true, true);
1250 return true;
1255 return false;
1258 bool readSvgNumberVector(const OUString& rCandidate, SvgNumberVector& rSvgNumberVector)
1260 const sal_Int32 nLen(rCandidate.getLength());
1261 rSvgNumberVector.clear();
1263 if(nLen)
1265 sal_Int32 nPos(0);
1266 SvgNumber aNum;
1267 skip_char(rCandidate, ' ', ',', nPos, nLen);
1269 while(readNumberAndUnit(rCandidate, nPos, aNum, nLen))
1271 rSvgNumberVector.push_back(aNum);
1272 skip_char(rCandidate, ' ', ',', nPos, nLen);
1275 return !rSvgNumberVector.empty();
1278 return false;
1281 SvgAspectRatio readSvgAspectRatio(const OUString& rCandidate)
1283 const sal_Int32 nLen(rCandidate.getLength());
1285 if(nLen)
1287 sal_Int32 nPos(0);
1288 SvgAlign aSvgAlign(Align_xMidYMid);
1289 bool bMeetOrSlice(true);
1290 bool bChanged(false);
1292 while(nPos < nLen)
1294 const sal_Int32 nInitPos(nPos);
1295 skip_char(rCandidate, ' ', nPos, nLen);
1296 OUStringBuffer aTokenName;
1297 copyString(rCandidate, nPos, aTokenName, nLen);
1299 if(!aTokenName.isEmpty())
1301 switch(StrToSVGToken(aTokenName.makeStringAndClear(), false))
1303 case SVGTokenDefer:
1305 bChanged = true;
1306 break;
1308 case SVGTokenNone:
1310 aSvgAlign = Align_none;
1311 bChanged = true;
1312 break;
1314 case SVGTokenXMinYMin:
1316 aSvgAlign = Align_xMinYMin;
1317 bChanged = true;
1318 break;
1320 case SVGTokenXMidYMin:
1322 aSvgAlign = Align_xMidYMin;
1323 bChanged = true;
1324 break;
1326 case SVGTokenXMaxYMin:
1328 aSvgAlign = Align_xMaxYMin;
1329 bChanged = true;
1330 break;
1332 case SVGTokenXMinYMid:
1334 aSvgAlign = Align_xMinYMid;
1335 bChanged = true;
1336 break;
1338 case SVGTokenXMidYMid:
1340 aSvgAlign = Align_xMidYMid;
1341 bChanged = true;
1342 break;
1344 case SVGTokenXMaxYMid:
1346 aSvgAlign = Align_xMaxYMid;
1347 bChanged = true;
1348 break;
1350 case SVGTokenXMinYMax:
1352 aSvgAlign = Align_xMinYMax;
1353 bChanged = true;
1354 break;
1356 case SVGTokenXMidYMax:
1358 aSvgAlign = Align_xMidYMax;
1359 bChanged = true;
1360 break;
1362 case SVGTokenXMaxYMax:
1364 aSvgAlign = Align_xMaxYMax;
1365 bChanged = true;
1366 break;
1368 case SVGTokenMeet:
1370 bMeetOrSlice = true;
1371 bChanged = true;
1372 break;
1374 case SVGTokenSlice:
1376 bMeetOrSlice = false;
1377 bChanged = true;
1378 break;
1380 default:
1382 break;
1387 if(nInitPos == nPos)
1389 OSL_ENSURE(false, "Could not interpret on current position (!)");
1390 nPos++;
1394 if(bChanged)
1396 return SvgAspectRatio(aSvgAlign, bMeetOrSlice);
1400 return SvgAspectRatio();
1403 bool readSvgStringVector(const OUString& rCandidate, SvgStringVector& rSvgStringVector)
1405 rSvgStringVector.clear();
1406 const sal_Int32 nLen(rCandidate.getLength());
1408 if(nLen)
1410 sal_Int32 nPos(0);
1411 OUStringBuffer aTokenValue;
1412 skip_char(rCandidate, ' ', ',', nPos, nLen);
1414 while(nPos < nLen)
1416 copyToLimiter(rCandidate, ',', nPos, aTokenValue, nLen);
1417 skip_char(rCandidate, ',', ' ', nPos, nLen);
1418 const OUString aString = aTokenValue.makeStringAndClear();
1420 if(!aString.isEmpty())
1422 rSvgStringVector.push_back(aString);
1427 return !rSvgStringVector.empty();
1430 void readImageLink(const OUString& rCandidate, OUString& rXLink, OUString& rUrl, OUString& rMimeType, OUString& rData)
1432 rXLink.clear();
1433 rUrl.clear();
1434 rMimeType.clear();
1435 rData.clear();
1437 if('#' == rCandidate[0])
1439 // local link
1440 rXLink = rCandidate.copy(1);
1442 else
1444 static const char aStrData[] = "data:";
1446 if(rCandidate.match(aStrData, 0))
1448 // embedded data
1449 sal_Int32 nPos(strlen(aStrData));
1450 sal_Int32 nLen(rCandidate.getLength());
1451 OUStringBuffer aBuffer;
1453 // read mime type
1454 skip_char(rCandidate, ' ', nPos, nLen);
1455 copyToLimiter(rCandidate, ';', nPos, aBuffer, nLen);
1456 skip_char(rCandidate, ' ', ';', nPos, nLen);
1457 rMimeType = aBuffer.makeStringAndClear();
1459 if(!rMimeType.isEmpty() && nPos < nLen)
1461 if(rMimeType.startsWith("image"))
1463 // image data
1464 OUString aData(rCandidate.copy(nPos));
1465 static const char aStrBase64[] = "base64";
1467 if(aData.startsWith(aStrBase64))
1469 // base64 encoded
1470 nPos = strlen(aStrBase64);
1471 nLen = aData.getLength();
1473 skip_char(aData, ' ', ',', nPos, nLen);
1475 if(nPos < nLen)
1477 rData = aData.copy(nPos);
1483 else
1485 // Url (path and filename)
1486 rUrl = rCandidate;
1491 OUString convert(const OUString& rCandidate, sal_Unicode nPattern, sal_Unicode nNew, bool bRemove)
1493 const sal_Int32 nLen(rCandidate.getLength());
1495 if(nLen)
1497 sal_Int32 nPos(0);
1498 OUStringBuffer aBuffer;
1499 bool bChanged(false);
1501 while(nPos < nLen)
1503 const sal_Unicode aChar(rCandidate[nPos]);
1505 if(nPattern == aChar)
1507 bChanged = true;
1509 if(!bRemove)
1511 aBuffer.append(nNew);
1514 else
1516 aBuffer.append(aChar);
1519 nPos++;
1522 if(bChanged)
1524 return aBuffer.makeStringAndClear();
1528 return rCandidate;
1531 // #i125325#
1532 OUString removeBlockComments(const OUString& rCandidate)
1534 const sal_Int32 nLen(rCandidate.getLength());
1536 if(nLen)
1538 sal_Int32 nPos(0);
1539 OUStringBuffer aBuffer;
1540 bool bChanged(false);
1541 sal_Int32 nInsideComment(0);
1542 const sal_Unicode aCommentSlash('/');
1543 const sal_Unicode aCommentStar('*');
1545 while(nPos < nLen)
1547 const sal_Unicode aChar(rCandidate[nPos]);
1548 const bool bStart(aCommentSlash == aChar && nPos + 1 < nLen && aCommentStar == rCandidate[nPos + 1]);
1549 const bool bEnd(aCommentStar == aChar && nPos + 1 < nLen && aCommentSlash == rCandidate[nPos + 1]);
1551 if(bStart)
1553 nPos += 2;
1554 nInsideComment++;
1555 bChanged = true;
1557 else if(bEnd)
1559 nPos += 2;
1560 nInsideComment--;
1562 else
1564 if(!nInsideComment)
1566 aBuffer.append(aChar);
1569 nPos++;
1573 if(bChanged)
1575 return aBuffer.makeStringAndClear();
1579 return rCandidate;
1582 OUString consolidateContiguousSpace(const OUString& rCandidate)
1584 const sal_Int32 nLen(rCandidate.getLength());
1586 if(nLen)
1588 sal_Int32 nPos(0);
1589 OUStringBuffer aBuffer;
1590 bool bInsideSpace(false);
1591 const sal_Unicode aSpace(' ');
1593 while(nPos < nLen)
1595 const sal_Unicode aChar(rCandidate[nPos]);
1597 if(aSpace == aChar)
1599 bInsideSpace = true;
1601 else
1603 if(bInsideSpace)
1605 bInsideSpace = false;
1606 aBuffer.append(aSpace);
1609 aBuffer.append(aChar);
1612 nPos++;
1615 if(bInsideSpace)
1617 aBuffer.append(aSpace);
1620 if(aBuffer.getLength() != nLen)
1622 return aBuffer.makeStringAndClear();
1626 return rCandidate;
1629 OUString whiteSpaceHandlingDefault(const OUString& rCandidate)
1631 const sal_Unicode aNewline('\n');
1632 const sal_Unicode aTab('\t');
1633 const sal_Unicode aSpace(' ');
1635 // remove all newline characters
1636 OUString aRetval(convert(rCandidate, aNewline, aNewline, true));
1638 // convert tab to space
1639 aRetval = convert(aRetval, aTab, aSpace, false);
1641 // strip of all leading and trailing spaces
1642 aRetval = aRetval.trim();
1644 // consolidate contiguous space
1645 aRetval = consolidateContiguousSpace(aRetval);
1647 return aRetval;
1650 OUString whiteSpaceHandlingPreserve(const OUString& rCandidate)
1652 const sal_Unicode aNewline('\n');
1653 const sal_Unicode aTab('\t');
1654 const sal_Unicode aSpace(' ');
1656 // convert newline to space
1657 convert(rCandidate, aNewline, aSpace, false);
1659 // convert tab to space
1660 convert(rCandidate, aTab, aSpace, false);
1662 return rCandidate;
1665 ::std::vector< double > solveSvgNumberVector(const SvgNumberVector& rInput, const InfoProvider& rInfoProvider)
1667 ::std::vector< double > aRetval;
1669 if(!rInput.empty())
1671 const double nCount(rInput.size());
1672 aRetval.reserve(nCount);
1674 for(sal_uInt32 a(0); a < nCount; a++)
1676 aRetval.push_back(rInput[a].solve(rInfoProvider));
1680 return aRetval;
1683 } // end of namespace svgio::svgreader
1685 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */