bump product version to 7.2.5.1
[LibreOffice.git] / svgio / source / svgreader / svgtools.cxx
blob5d6534db07214c19fbaa5c21721d89bb358c604c
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(SvgAlign::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 SvgAlign::xMidYMin:
83 case SvgAlign::xMidYMid:
84 case SvgAlign::xMidYMax:
86 // centerX
87 const double fFreeSpace(rTarget.getWidth() - fNewWidth);
88 fTransX = fFreeSpace * 0.5;
89 break;
91 case SvgAlign::xMaxYMin:
92 case SvgAlign::xMaxYMid:
93 case SvgAlign::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 SvgAlign::xMinYMid:
110 case SvgAlign::xMidYMid:
111 case SvgAlign::xMaxYMid:
113 // centerY
114 const double fFreeSpace(rTarget.getHeight() - fNewHeight);
115 fTransY = fFreeSpace * 0.5;
116 break;
118 case SvgAlign::xMinYMax:
119 case SvgAlign::xMidYMax:
120 case SvgAlign::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 void skip_char(std::u16string_view rCandidate, sal_Unicode nChar, sal_Int32& nPos, const sal_Int32 nLen)
140 while(nPos < nLen && nChar == rCandidate[nPos])
142 nPos++;
146 void skip_char(std::u16string_view rCandidate, sal_Unicode nCharA, sal_Unicode nCharB, sal_Int32& nPos, const sal_Int32 nLen)
148 while(nPos < nLen && (nCharA == rCandidate[nPos] || nCharB == rCandidate[nPos]))
150 nPos++;
154 void copySign(std::u16string_view rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
156 if(nPos < nLen)
158 const sal_Unicode aChar(rCandidate[nPos]);
160 if('+' == aChar || '-' == aChar)
162 rTarget.append(aChar);
163 nPos++;
168 void copyNumber(std::u16string_view rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
170 bool bOnNumber(true);
172 while(bOnNumber && nPos < nLen)
174 const sal_Unicode aChar(rCandidate[nPos]);
176 bOnNumber = ('0' <= aChar && '9' >= aChar) || '.' == aChar;
178 if(bOnNumber)
180 rTarget.append(aChar);
181 nPos++;
186 void copyHex(std::u16string_view rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
188 bool bOnHex(true);
190 while(bOnHex && nPos < nLen)
192 const sal_Unicode aChar(rCandidate[nPos]);
194 bOnHex = ('0' <= aChar && '9' >= aChar)
195 || ('A' <= aChar && 'F' >= aChar)
196 || ('a' <= aChar && 'f' >= aChar);
198 if(bOnHex)
200 rTarget.append(aChar);
201 nPos++;
206 void copyString(std::u16string_view rCandidate, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
208 bool bOnChar(true);
210 while(bOnChar && nPos < nLen)
212 const sal_Unicode aChar(rCandidate[nPos]);
214 bOnChar = ('a' <= aChar && 'z' >= aChar)
215 || ('A' <= aChar && 'Z' >= aChar)
216 || '-' == aChar;
218 if(bOnChar)
220 rTarget.append(aChar);
221 nPos++;
226 void copyToLimiter(std::u16string_view rCandidate, sal_Unicode nLimiter, sal_Int32& nPos, OUStringBuffer& rTarget, const sal_Int32 nLen)
228 while(nPos < nLen && nLimiter != rCandidate[nPos])
230 rTarget.append(rCandidate[nPos]);
231 nPos++;
235 bool readNumber(std::u16string_view rCandidate, sal_Int32& nPos, double& fNum, const sal_Int32 nLen)
237 if(nPos < nLen)
239 OUStringBuffer aNum;
241 copySign(rCandidate, nPos, aNum, nLen);
242 copyNumber(rCandidate, nPos, aNum, nLen);
244 if(nPos < nLen)
246 const sal_Unicode aChar(rCandidate[nPos]);
248 if('e' == aChar || 'E' == aChar)
250 // try to read exponential number, but be careful. I had
251 // a case where dx="2em" was used, thus the 'e' was consumed
252 // by error. First try if there are numbers after the 'e',
253 // safe current state
254 nPos++;
255 const OUStringBuffer aNum2(aNum);
256 const sal_Int32 nPosAfterE(nPos);
258 aNum.append(aChar);
259 copySign(rCandidate, nPos, aNum, nLen);
260 copyNumber(rCandidate, nPos, aNum, nLen);
262 if(nPosAfterE == nPos)
264 // no number after 'e', go back. Do not
265 // return false, it's still a valid integer number
266 aNum = aNum2;
267 nPos--;
272 if(!aNum.isEmpty())
274 rtl_math_ConversionStatus eStatus;
276 fNum = rtl::math::stringToDouble(
277 aNum.makeStringAndClear(), '.', ',',
278 &eStatus);
280 return eStatus == rtl_math_ConversionStatus_Ok;
284 return false;
287 SvgUnit readUnit(std::u16string_view rCandidate, sal_Int32& nPos, const sal_Int32 nLen)
289 SvgUnit aRetval(SvgUnit::px);
291 if(nPos < nLen)
293 const sal_Unicode aCharA(rCandidate[nPos]);
295 if(nPos + 1 < nLen)
297 const sal_Unicode aCharB(rCandidate[nPos + 1]);
298 bool bTwoCharValid(false);
300 switch(aCharA)
302 case u'e' :
304 if('m' == aCharB)
306 // 'em' Relative to current font size
307 aRetval = SvgUnit::em;
308 bTwoCharValid = true;
310 else if('x' == aCharB)
312 // 'ex' Relative to current font x-height
313 aRetval = SvgUnit::ex;
314 bTwoCharValid = true;
316 break;
318 case u'p' :
320 if('x' == aCharB)
322 // 'px' UserUnit (default)
323 bTwoCharValid = true;
325 else if('t' == aCharB)
327 // 'pt' == 4/3 px
328 aRetval = SvgUnit::pt;
329 bTwoCharValid = true;
331 else if('c' == aCharB)
333 // 'pc' == 16 px
334 aRetval = SvgUnit::pc;
335 bTwoCharValid = true;
337 break;
339 case u'i' :
341 if('n' == aCharB)
343 // 'in' == 96 px, since CSS 2.1
344 aRetval = SvgUnit::in;
345 bTwoCharValid = true;
347 break;
349 case u'c' :
351 if('m' == aCharB)
353 // 'cm' == 37.79527559 px
354 aRetval = SvgUnit::cm;
355 bTwoCharValid = true;
357 break;
359 case u'm' :
361 if('m' == aCharB)
363 // 'mm' == 3.779528 px
364 aRetval = SvgUnit::mm;
365 bTwoCharValid = true;
367 break;
371 if(bTwoCharValid)
373 nPos += 2;
376 else
378 if('%' == aCharA)
380 // percent used, relative to current
381 nPos++;
382 aRetval = SvgUnit::percent;
387 return aRetval;
390 bool readNumberAndUnit(std::u16string_view rCandidate, sal_Int32& nPos, SvgNumber& aNum, const sal_Int32 nLen)
392 double fNum(0.0);
394 if(readNumber(rCandidate, nPos, fNum, nLen))
396 skip_char(rCandidate, ' ', nPos, nLen);
397 aNum = SvgNumber(fNum, readUnit(rCandidate, nPos, nLen));
399 return true;
402 return false;
405 bool readAngle(const OUString& rCandidate, sal_Int32& nPos, double& fAngle, const sal_Int32 nLen)
407 if(readNumber(rCandidate, nPos, fAngle, nLen))
409 skip_char(rCandidate, ' ', nPos, nLen);
411 enum class DegreeType
413 deg,
414 grad,
416 } aType(DegreeType::deg); // degrees is default
418 if(nPos < nLen)
420 const sal_Unicode aChar(rCandidate[nPos]);
421 static const char aStrGrad[] = "grad";
422 static const char aStrRad[] = "rad";
424 switch(aChar)
426 case u'g' :
427 case u'G' :
429 if(rCandidate.matchIgnoreAsciiCase(aStrGrad, nPos))
431 // angle in grad
432 nPos += strlen(aStrGrad);
433 aType = DegreeType::grad;
435 break;
437 case u'r' :
438 case u'R' :
440 if(rCandidate.matchIgnoreAsciiCase(aStrRad, nPos))
442 // angle in radians
443 nPos += strlen(aStrRad);
444 aType = DegreeType::rad;
446 break;
451 // convert to radians
452 if (DegreeType::deg == aType)
454 fAngle = basegfx::deg2rad(fAngle);
456 else if (DegreeType::grad == aType)
458 // looks like 100 grad is 90 degrees
459 fAngle *= F_PI / 200.0;
462 return true;
465 return false;
468 sal_Int32 read_hex(sal_Unicode nChar)
470 if(nChar >= '0' && nChar <= '9')
472 return nChar - u'0';
474 else if(nChar >= 'A' && nChar <= 'F')
476 return 10 + sal_Int32(nChar - u'A');
478 else if(nChar >= 'a' && nChar <= 'f')
480 return 10 + sal_Int32(nChar - u'a');
482 else
484 // error
485 return 0;
489 bool match_colorKeyword(basegfx::BColor& rColor, const OUString& rName, bool bCaseIndependent)
491 typedef std::unordered_map< OUString, Color > ColorTokenMapper;
492 typedef std::pair< OUString, Color > ColorTokenValueType;
493 static const ColorTokenMapper aColorTokenMapperList {
494 { ColorTokenValueType(OUString("aliceblue"), Color(240, 248, 255)) },
495 { ColorTokenValueType(OUString("antiquewhite"), Color(250, 235, 215) ) },
496 { ColorTokenValueType(OUString("aqua"), Color( 0, 255, 255) ) },
497 { ColorTokenValueType(OUString("aquamarine"), Color(127, 255, 212) ) },
498 { ColorTokenValueType(OUString("azure"), Color(240, 255, 255) ) },
499 { ColorTokenValueType(OUString("beige"), Color(245, 245, 220) ) },
500 { ColorTokenValueType(OUString("bisque"), Color(255, 228, 196) ) },
501 { ColorTokenValueType(OUString("black"), Color( 0, 0, 0) ) },
502 { ColorTokenValueType(OUString("blanchedalmond"), Color(255, 235, 205) ) },
503 { ColorTokenValueType(OUString("blue"), Color( 0, 0, 255) ) },
504 { ColorTokenValueType(OUString("blueviolet"), Color(138, 43, 226) ) },
505 { ColorTokenValueType(OUString("brown"), Color(165, 42, 42) ) },
506 { ColorTokenValueType(OUString("burlywood"), Color(222, 184, 135) ) },
507 { ColorTokenValueType(OUString("cadetblue"), Color( 95, 158, 160) ) },
508 { ColorTokenValueType(OUString("chartreuse"), Color(127, 255, 0) ) },
509 { ColorTokenValueType(OUString("chocolate"), Color(210, 105, 30) ) },
510 { ColorTokenValueType(OUString("coral"), Color(255, 127, 80) ) },
511 { ColorTokenValueType(OUString("cornflowerblue"), Color(100, 149, 237) ) },
512 { ColorTokenValueType(OUString("cornsilk"), Color(255, 248, 220) ) },
513 { ColorTokenValueType(OUString("crimson"), Color(220, 20, 60) ) },
514 { ColorTokenValueType(OUString("cyan"), Color( 0, 255, 255) ) },
515 { ColorTokenValueType(OUString("darkblue"), Color( 0, 0, 139) ) },
516 { ColorTokenValueType(OUString("darkcyan"), Color( 0, 139, 139) ) },
517 { ColorTokenValueType(OUString("darkgoldenrod"), Color(184, 134, 11) ) },
518 { ColorTokenValueType(OUString("darkgray"), Color(169, 169, 169) ) },
519 { ColorTokenValueType(OUString("darkgreen"), Color( 0, 100, 0) ) },
520 { ColorTokenValueType(OUString("darkgrey"), Color(169, 169, 169) ) },
521 { ColorTokenValueType(OUString("darkkhaki"), Color(189, 183, 107) ) },
522 { ColorTokenValueType(OUString("darkmagenta"), Color(139, 0, 139) ) },
523 { ColorTokenValueType(OUString("darkolivegreen"), Color( 85, 107, 47) ) },
524 { ColorTokenValueType(OUString("darkorange"), Color(255, 140, 0) ) },
525 { ColorTokenValueType(OUString("darkorchid"), Color(153, 50, 204) ) },
526 { ColorTokenValueType(OUString("darkred"), Color(139, 0, 0) ) },
527 { ColorTokenValueType(OUString("darksalmon"), Color(233, 150, 122) ) },
528 { ColorTokenValueType(OUString("darkseagreen"), Color(143, 188, 143) ) },
529 { ColorTokenValueType(OUString("darkslateblue"), Color( 72, 61, 139) ) },
530 { ColorTokenValueType(OUString("darkslategray"), Color( 47, 79, 79) ) },
531 { ColorTokenValueType(OUString("darkslategrey"), Color( 47, 79, 79) ) },
532 { ColorTokenValueType(OUString("darkturquoise"), Color( 0, 206, 209) ) },
533 { ColorTokenValueType(OUString("darkviolet"), Color(148, 0, 211) ) },
534 { ColorTokenValueType(OUString("deeppink"), Color(255, 20, 147) ) },
535 { ColorTokenValueType(OUString("deepskyblue"), Color( 0, 191, 255) ) },
536 { ColorTokenValueType(OUString("dimgray"), Color(105, 105, 105) ) },
537 { ColorTokenValueType(OUString("dimgrey"), Color(105, 105, 105) ) },
538 { ColorTokenValueType(OUString("dodgerblue"), Color( 30, 144, 255) ) },
539 { ColorTokenValueType(OUString("firebrick"), Color(178, 34, 34) ) },
540 { ColorTokenValueType(OUString("floralwhite"), Color(255, 250, 240) ) },
541 { ColorTokenValueType(OUString("forestgreen"), Color( 34, 139, 34) ) },
542 { ColorTokenValueType(OUString("fuchsia"), Color(255, 0, 255) ) },
543 { ColorTokenValueType(OUString("gainsboro"), Color(220, 220, 220) ) },
544 { ColorTokenValueType(OUString("ghostwhite"), Color(248, 248, 255) ) },
545 { ColorTokenValueType(OUString("gold"), Color(255, 215, 0) ) },
546 { ColorTokenValueType(OUString("goldenrod"), Color(218, 165, 32) ) },
547 { ColorTokenValueType(OUString("gray"), Color(128, 128, 128) ) },
548 { ColorTokenValueType(OUString("grey"), Color(128, 128, 128) ) },
549 { ColorTokenValueType(OUString("green"), Color(0, 128, 0) ) },
550 { ColorTokenValueType(OUString("greenyellow"), Color(173, 255, 47) ) },
551 { ColorTokenValueType(OUString("honeydew"), Color(240, 255, 240) ) },
552 { ColorTokenValueType(OUString("hotpink"), Color(255, 105, 180) ) },
553 { ColorTokenValueType(OUString("indianred"), Color(205, 92, 92) ) },
554 { ColorTokenValueType(OUString("indigo"), Color( 75, 0, 130) ) },
555 { ColorTokenValueType(OUString("ivory"), Color(255, 255, 240) ) },
556 { ColorTokenValueType(OUString("khaki"), Color(240, 230, 140) ) },
557 { ColorTokenValueType(OUString("lavender"), Color(230, 230, 250) ) },
558 { ColorTokenValueType(OUString("lavenderblush"), Color(255, 240, 245) ) },
559 { ColorTokenValueType(OUString("lawngreen"), Color(124, 252, 0) ) },
560 { ColorTokenValueType(OUString("lemonchiffon"), Color(255, 250, 205) ) },
561 { ColorTokenValueType(OUString("lightblue"), Color(173, 216, 230) ) },
562 { ColorTokenValueType(OUString("lightcoral"), Color(240, 128, 128) ) },
563 { ColorTokenValueType(OUString("lightcyan"), Color(224, 255, 255) ) },
564 { ColorTokenValueType(OUString("lightgoldenrodyellow"), Color(250, 250, 210) ) },
565 { ColorTokenValueType(OUString("lightgray"), Color(211, 211, 211) ) },
566 { ColorTokenValueType(OUString("lightgreen"), Color(144, 238, 144) ) },
567 { ColorTokenValueType(OUString("lightgrey"), Color(211, 211, 211) ) },
568 { ColorTokenValueType(OUString("lightpink"), Color(255, 182, 193) ) },
569 { ColorTokenValueType(OUString("lightsalmon"), Color(255, 160, 122) ) },
570 { ColorTokenValueType(OUString("lightseagreen"), Color( 32, 178, 170) ) },
571 { ColorTokenValueType(OUString("lightskyblue"), Color(135, 206, 250) ) },
572 { ColorTokenValueType(OUString("lightslategray"), Color(119, 136, 153) ) },
573 { ColorTokenValueType(OUString("lightslategrey"), Color(119, 136, 153) ) },
574 { ColorTokenValueType(OUString("lightsteelblue"), Color(176, 196, 222) ) },
575 { ColorTokenValueType(OUString("lightyellow"), Color(255, 255, 224) ) },
576 { ColorTokenValueType(OUString("lime"), Color( 0, 255, 0) ) },
577 { ColorTokenValueType(OUString("limegreen"), Color( 50, 205, 50) ) },
578 { ColorTokenValueType(OUString("linen"), Color(250, 240, 230) ) },
579 { ColorTokenValueType(OUString("magenta"), Color(255, 0, 255) ) },
580 { ColorTokenValueType(OUString("maroon"), Color(128, 0, 0) ) },
581 { ColorTokenValueType(OUString("mediumaquamarine"), Color(102, 205, 170) ) },
582 { ColorTokenValueType(OUString("mediumblue"), Color( 0, 0, 205) ) },
583 { ColorTokenValueType(OUString("mediumorchid"), Color(186, 85, 211) ) },
584 { ColorTokenValueType(OUString("mediumpurple"), Color(147, 112, 219) ) },
585 { ColorTokenValueType(OUString("mediumseagreen"), Color( 60, 179, 113) ) },
586 { ColorTokenValueType(OUString("mediumslateblue"), Color(123, 104, 238) ) },
587 { ColorTokenValueType(OUString("mediumspringgreen"), Color( 0, 250, 154) ) },
588 { ColorTokenValueType(OUString("mediumturquoise"), Color( 72, 209, 204) ) },
589 { ColorTokenValueType(OUString("mediumvioletred"), Color(199, 21, 133) ) },
590 { ColorTokenValueType(OUString("midnightblue"), Color( 25, 25, 112) ) },
591 { ColorTokenValueType(OUString("mintcream"), Color(245, 255, 250) ) },
592 { ColorTokenValueType(OUString("mistyrose"), Color(255, 228, 225) ) },
593 { ColorTokenValueType(OUString("moccasin"), Color(255, 228, 181) ) },
594 { ColorTokenValueType(OUString("navajowhite"), Color(255, 222, 173) ) },
595 { ColorTokenValueType(OUString("navy"), Color( 0, 0, 128) ) },
596 { ColorTokenValueType(OUString("oldlace"), Color(253, 245, 230) ) },
597 { ColorTokenValueType(OUString("olive"), Color(128, 128, 0) ) },
598 { ColorTokenValueType(OUString("olivedrab"), Color(107, 142, 35) ) },
599 { ColorTokenValueType(OUString("orange"), Color(255, 165, 0) ) },
600 { ColorTokenValueType(OUString("orangered"), Color(255, 69, 0) ) },
601 { ColorTokenValueType(OUString("orchid"), Color(218, 112, 214) ) },
602 { ColorTokenValueType(OUString("palegoldenrod"), Color(238, 232, 170) ) },
603 { ColorTokenValueType(OUString("palegreen"), Color(152, 251, 152) ) },
604 { ColorTokenValueType(OUString("paleturquoise"), Color(175, 238, 238) ) },
605 { ColorTokenValueType(OUString("palevioletred"), Color(219, 112, 147) ) },
606 { ColorTokenValueType(OUString("papayawhip"), Color(255, 239, 213) ) },
607 { ColorTokenValueType(OUString("peachpuff"), Color(255, 218, 185) ) },
608 { ColorTokenValueType(OUString("peru"), Color(205, 133, 63) ) },
609 { ColorTokenValueType(OUString("pink"), Color(255, 192, 203) ) },
610 { ColorTokenValueType(OUString("plum"), Color(221, 160, 221) ) },
611 { ColorTokenValueType(OUString("powderblue"), Color(176, 224, 230) ) },
612 { ColorTokenValueType(OUString("purple"), Color(128, 0, 128) ) },
613 { ColorTokenValueType(OUString("red"), Color(255, 0, 0) ) },
614 { ColorTokenValueType(OUString("rosybrown"), Color(188, 143, 143) ) },
615 { ColorTokenValueType(OUString("royalblue"), Color( 65, 105, 225) ) },
616 { ColorTokenValueType(OUString("saddlebrown"), Color(139, 69, 19) ) },
617 { ColorTokenValueType(OUString("salmon"), Color(250, 128, 114) ) },
618 { ColorTokenValueType(OUString("sandybrown"), Color(244, 164, 96) ) },
619 { ColorTokenValueType(OUString("seagreen"), Color( 46, 139, 87) ) },
620 { ColorTokenValueType(OUString("seashell"), Color(255, 245, 238) ) },
621 { ColorTokenValueType(OUString("sienna"), Color(160, 82, 45) ) },
622 { ColorTokenValueType(OUString("silver"), Color(192, 192, 192) ) },
623 { ColorTokenValueType(OUString("skyblue"), Color(135, 206, 235) ) },
624 { ColorTokenValueType(OUString("slateblue"), Color(106, 90, 205) ) },
625 { ColorTokenValueType(OUString("slategray"), Color(112, 128, 144) ) },
626 { ColorTokenValueType(OUString("slategrey"), Color(112, 128, 144) ) },
627 { ColorTokenValueType(OUString("snow"), Color(255, 250, 250) ) },
628 { ColorTokenValueType(OUString("springgreen"), Color( 0, 255, 127) ) },
629 { ColorTokenValueType(OUString("steelblue"), Color( 70, 130, 180) ) },
630 { ColorTokenValueType(OUString("tan"), Color(210, 180, 140) ) },
631 { ColorTokenValueType(OUString("teal"), Color( 0, 128, 128) ) },
632 { ColorTokenValueType(OUString("thistle"), Color(216, 191, 216) ) },
633 { ColorTokenValueType(OUString("tomato"), Color(255, 99, 71) ) },
634 { ColorTokenValueType(OUString("turquoise"), Color( 64, 224, 208) ) },
635 { ColorTokenValueType(OUString("violet"), Color(238, 130, 238) ) },
636 { ColorTokenValueType(OUString("wheat"), Color(245, 222, 179) ) },
637 { ColorTokenValueType(OUString("white"), Color(255, 255, 255) ) },
638 { ColorTokenValueType(OUString("whitesmoke"), Color(245, 245, 245) ) },
639 { ColorTokenValueType(OUString("yellow"), Color(255, 255, 0) ) },
640 { ColorTokenValueType(OUString("yellowgreen"), Color(154, 205, 50) ) },
643 ColorTokenMapper::const_iterator aResult(aColorTokenMapperList.find(rName));
645 if(bCaseIndependent && aResult == aColorTokenMapperList.end())
647 // also try case independent match (e.g. for Css styles)
648 aResult = aColorTokenMapperList.find(rName.toAsciiLowerCase());
651 if(aResult == aColorTokenMapperList.end())
653 return false;
655 else
657 rColor = aResult->second.getBColor();
658 return true;
662 bool read_color(const OUString& rCandidate, basegfx::BColor& rColor, bool bCaseIndependent, SvgNumber& rOpacity)
664 const sal_Int32 nLen(rCandidate.getLength());
666 if(nLen)
668 const sal_Unicode aChar(rCandidate[0]);
669 const double fFactor(1.0 / 255.0);
671 if(aChar == '#')
673 // hex definition
674 OUStringBuffer aNum;
675 sal_Int32 nPos(1);
677 copyHex(rCandidate, nPos, aNum, nLen);
678 const sal_Int32 nLength(aNum.getLength());
680 if(3 == nLength)
682 const sal_Int32 nR(read_hex(aNum[0]));
683 const sal_Int32 nG(read_hex(aNum[1]));
684 const sal_Int32 nB(read_hex(aNum[2]));
686 rColor.setRed((nR | (nR << 4)) * fFactor);
687 rColor.setGreen((nG | (nG << 4)) * fFactor);
688 rColor.setBlue((nB | (nB << 4)) * fFactor);
690 return true;
692 else if(6 == nLength)
694 const sal_Int32 nR1(read_hex(aNum[0]));
695 const sal_Int32 nR2(read_hex(aNum[1]));
696 const sal_Int32 nG1(read_hex(aNum[2]));
697 const sal_Int32 nG2(read_hex(aNum[3]));
698 const sal_Int32 nB1(read_hex(aNum[4]));
699 const sal_Int32 nB2(read_hex(aNum[5]));
701 rColor.setRed((nR2 | (nR1 << 4)) * fFactor);
702 rColor.setGreen((nG2 | (nG1 << 4)) * fFactor);
703 rColor.setBlue((nB2 | (nB1 << 4)) * fFactor);
705 return true;
708 else
710 static const char aStrRgb[] = "rgb";
712 if(rCandidate.matchIgnoreAsciiCase(aStrRgb, 0))
714 // rgb/rgba definition
715 sal_Int32 nPos(strlen(aStrRgb));
716 bool bIsRGBA = false;
718 if('a' == rCandidate[nPos])
720 //Delete the 'a' from 'rbga'
721 skip_char(rCandidate, 'a', nPos, nPos + 1);
722 bIsRGBA = true;
725 skip_char(rCandidate, ' ', '(', nPos, nLen);
726 double fR(0.0);
728 if(readNumber(rCandidate, nPos, fR, nLen))
730 skip_char(rCandidate, ' ', nPos, nLen);
732 if(nPos < nLen)
734 const sal_Unicode aPercentChar(rCandidate[nPos]);
735 const bool bIsPercent('%' == aPercentChar);
736 double fG(0.0);
738 if(bIsPercent)
740 skip_char(rCandidate, '%', nPos, nLen);
743 skip_char(rCandidate, ' ', ',', nPos, nLen);
745 if(readNumber(rCandidate, nPos, fG, nLen))
747 double fB(0.0);
749 if(bIsPercent)
751 skip_char(rCandidate, '%', nPos, nLen);
754 skip_char(rCandidate, ' ', ',', nPos, nLen);
756 if(readNumber(rCandidate, nPos, fB, nLen))
758 double fA(1.0);
760 if(bIsPercent)
762 skip_char(rCandidate, '%', nPos, nLen);
765 skip_char(rCandidate, ' ', ',', nPos, nLen);
767 if(readNumber(rCandidate, nPos, fA, nLen))
769 if(bIsRGBA)
771 const double fFac(bIsPercent ? 0.01 : 1);
772 rOpacity = SvgNumber(fA * fFac);
774 if(bIsPercent)
776 skip_char(rCandidate, '%', nPos, nLen);
779 else
781 return false;
785 const double fFac(bIsPercent ? 0.01 : fFactor);
787 rColor.setRed(fR * fFac);
788 rColor.setGreen(fG * fFac);
789 rColor.setBlue(fB * fFac);
791 skip_char(rCandidate, ' ', ')', nPos, nLen);
792 return true;
798 else
800 // color keyword
801 if(match_colorKeyword(rColor, rCandidate, bCaseIndependent))
803 return true;
809 return false;
812 basegfx::B2DRange readViewBox(const OUString& rCandidate, InfoProvider const & rInfoProvider)
814 const sal_Int32 nLen(rCandidate.getLength());
816 if(nLen)
818 sal_Int32 nPos(0);
819 SvgNumber aMinX;
820 skip_char(rCandidate, ' ', ',', nPos, nLen);
822 if(readNumberAndUnit(rCandidate, nPos, aMinX, nLen))
824 SvgNumber aMinY;
825 skip_char(rCandidate, ' ', ',', nPos, nLen);
827 if(readNumberAndUnit(rCandidate, nPos, aMinY, nLen))
829 SvgNumber aWidth;
830 skip_char(rCandidate, ' ', ',', nPos, nLen);
832 if(readNumberAndUnit(rCandidate, nPos, aWidth, nLen))
834 SvgNumber aHeight;
835 skip_char(rCandidate, ' ', ',', nPos, nLen);
837 if(readNumberAndUnit(rCandidate, nPos, aHeight, nLen))
839 double fX(aMinX.solve(rInfoProvider, NumberType::xcoordinate));
840 double fY(aMinY.solve(rInfoProvider, NumberType::ycoordinate));
841 double fW(aWidth.solve(rInfoProvider, NumberType::xcoordinate));
842 double fH(aHeight.solve(rInfoProvider, NumberType::ycoordinate));
843 return basegfx::B2DRange(fX,fY,fX+fW,fY+fH);
850 return basegfx::B2DRange();
853 basegfx::B2DHomMatrix readTransform(const OUString& rCandidate, InfoProvider const & rInfoProvider)
855 basegfx::B2DHomMatrix aMatrix;
856 const sal_Int32 nLen(rCandidate.getLength());
858 if(nLen)
860 sal_Int32 nPos(0);
861 skip_char(rCandidate, ' ', ',', nPos, nLen);
863 while(nPos < nLen)
865 const sal_Unicode aChar(rCandidate[nPos]);
866 const sal_Int32 nInitPos(nPos);
867 static const char aStrMatrix[] = "matrix";
868 static const char aStrTranslate[] = "translate";
869 static const char aStrScale[] = "scale";
870 static const char aStrRotate[] = "rotate";
871 static const char aStrSkewX[] = "skewX";
872 static const char aStrSkewY[] = "skewY";
874 switch(aChar)
876 case u'm' :
878 if(rCandidate.match(aStrMatrix, nPos))
880 // matrix element
881 nPos += strlen(aStrMatrix);
882 skip_char(rCandidate, ' ', '(', nPos, nLen);
883 SvgNumber aVal;
884 basegfx::B2DHomMatrix aNew;
886 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
888 aNew.set(0, 0, aVal.solve(rInfoProvider)); // Element A
889 skip_char(rCandidate, ' ', ',', nPos, nLen);
891 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
893 aNew.set(1, 0, aVal.solve(rInfoProvider)); // Element B
894 skip_char(rCandidate, ' ', ',', nPos, nLen);
896 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
898 aNew.set(0, 1, aVal.solve(rInfoProvider)); // Element C
899 skip_char(rCandidate, ' ', ',', nPos, nLen);
901 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
903 aNew.set(1, 1, aVal.solve(rInfoProvider)); // Element D
904 skip_char(rCandidate, ' ', ',', nPos, nLen);
906 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
908 aNew.set(0, 2, aVal.solve(rInfoProvider, NumberType::xcoordinate)); // Element E
909 skip_char(rCandidate, ' ', ',', nPos, nLen);
911 if(readNumberAndUnit(rCandidate, nPos, aVal, nLen))
913 aNew.set(1, 2, aVal.solve(rInfoProvider, NumberType::ycoordinate)); // Element F
914 skip_char(rCandidate, ' ', ')', nPos, nLen);
915 skip_char(rCandidate, ' ', ',', nPos, nLen);
917 // caution: String is evaluated from left to right, but matrix multiplication
918 // in SVG is right to left, so put the new transformation before the current
919 // one by multiplicating from the right side
920 aMatrix = aMatrix * aNew;
928 break;
930 case u't' :
932 if(rCandidate.match(aStrTranslate, nPos))
934 // translate element
935 nPos += strlen(aStrTranslate);
936 skip_char(rCandidate, ' ', '(', nPos, nLen);
937 SvgNumber aTransX;
939 if(readNumberAndUnit(rCandidate, nPos, aTransX, nLen))
941 skip_char(rCandidate, ' ', ',', nPos, nLen);
942 SvgNumber aTransY;
943 readNumberAndUnit(rCandidate, nPos, aTransY, nLen);
944 skip_char(rCandidate, ' ', ')', nPos, nLen);
945 skip_char(rCandidate, ' ', ',', nPos, nLen);
947 aMatrix = aMatrix * basegfx::utils::createTranslateB2DHomMatrix(
948 aTransX.solve(rInfoProvider, NumberType::xcoordinate),
949 aTransY.solve(rInfoProvider, NumberType::ycoordinate));
952 break;
954 case u's' :
956 if(rCandidate.match(aStrScale, nPos))
958 // scale element
959 nPos += strlen(aStrScale);
960 skip_char(rCandidate, ' ', '(', nPos, nLen);
961 SvgNumber aScaleX;
963 if(readNumberAndUnit(rCandidate, nPos, aScaleX, nLen))
965 skip_char(rCandidate, ' ', ',', nPos, nLen);
966 SvgNumber aScaleY(aScaleX);
967 readNumberAndUnit(rCandidate, nPos, aScaleY, nLen);
968 skip_char(rCandidate, ' ', ')', nPos, nLen);
969 skip_char(rCandidate, ' ', ',', nPos, nLen);
971 aMatrix = aMatrix * basegfx::utils::createScaleB2DHomMatrix(
972 aScaleX.solve(rInfoProvider),
973 aScaleY.solve(rInfoProvider));
976 else if(rCandidate.match(aStrSkewX, nPos))
978 // skewx element
979 nPos += strlen(aStrSkewX);
980 skip_char(rCandidate, ' ', '(', nPos, nLen);
981 double fSkewX(0.0);
983 if(readAngle(rCandidate, nPos, fSkewX, nLen))
985 skip_char(rCandidate, ' ', ')', nPos, nLen);
986 skip_char(rCandidate, ' ', ',', nPos, nLen);
988 aMatrix = aMatrix * basegfx::utils::createShearXB2DHomMatrix(tan(fSkewX));
991 else if(rCandidate.match(aStrSkewY, nPos))
993 // skewy element
994 nPos += strlen(aStrSkewY);
995 skip_char(rCandidate, ' ', '(', nPos, nLen);
996 double fSkewY(0.0);
998 if(readAngle(rCandidate, nPos, fSkewY, nLen))
1000 skip_char(rCandidate, ' ', ')', nPos, nLen);
1001 skip_char(rCandidate, ' ', ',', nPos, nLen);
1003 aMatrix = aMatrix * basegfx::utils::createShearYB2DHomMatrix(tan(fSkewY));
1006 break;
1008 case u'r' :
1010 if(rCandidate.match(aStrRotate, nPos))
1012 // rotate element
1013 nPos += strlen(aStrRotate);
1014 skip_char(rCandidate, ' ', '(', nPos, nLen);
1015 double fAngle(0.0);
1017 if(readAngle(rCandidate, nPos, fAngle, nLen))
1019 skip_char(rCandidate, ' ', ',', nPos, nLen);
1020 SvgNumber aX;
1021 readNumberAndUnit(rCandidate, nPos, aX, nLen);
1022 skip_char(rCandidate, ' ', ',', nPos, nLen);
1023 SvgNumber aY;
1024 readNumberAndUnit(rCandidate, nPos, aY, nLen);
1025 skip_char(rCandidate, ' ', ')', nPos, nLen);
1026 skip_char(rCandidate, ' ', ',', nPos, nLen);
1028 const double fX(aX.isSet() ? aX.solve(rInfoProvider, NumberType::xcoordinate) : 0.0);
1029 const double fY(aY.isSet() ? aY.solve(rInfoProvider, NumberType::ycoordinate) : 0.0);
1031 if(!basegfx::fTools::equalZero(fX) || !basegfx::fTools::equalZero(fY))
1033 // rotate around point
1034 aMatrix = aMatrix * basegfx::utils::createRotateAroundPoint(fX, fY, fAngle);
1036 else
1038 // rotate
1039 aMatrix = aMatrix * basegfx::utils::createRotateB2DHomMatrix(fAngle);
1043 break;
1047 if(nInitPos == nPos)
1049 SAL_WARN("svgio", "Could not interpret on current position (!)");
1050 nPos++;
1055 return aMatrix;
1058 bool readSingleNumber(const OUString& rCandidate, SvgNumber& aNum)
1060 const sal_Int32 nLen(rCandidate.getLength());
1061 sal_Int32 nPos(0);
1063 return readNumberAndUnit(rCandidate, nPos, aNum, nLen);
1066 bool readLocalUrl(const OUString& rCandidate, OUString& rURL)
1068 static const char aStrUrl[] = "url";
1070 if(rCandidate.startsWith(aStrUrl))
1072 const sal_Int32 nLen(rCandidate.getLength());
1073 sal_Int32 nPos(strlen(aStrUrl));
1075 skip_char(rCandidate, '(', '#', nPos, nLen);
1076 OUStringBuffer aTokenValue;
1077 copyToLimiter(rCandidate, ')', nPos, aTokenValue, nLen);
1078 rURL = aTokenValue.makeStringAndClear();
1080 return true;
1083 return false;
1086 bool readSvgPaint(const OUString& rCandidate, SvgPaint& rSvgPaint,
1087 OUString& rURL, bool bCaseIndependent, SvgNumber& rOpacity)
1089 if( !rCandidate.isEmpty() )
1091 basegfx::BColor aColor;
1093 if(read_color(rCandidate, aColor, bCaseIndependent, rOpacity))
1095 rSvgPaint = SvgPaint(aColor, true, true);
1096 return true;
1098 else
1100 if(rCandidate.startsWith("none"))
1102 rSvgPaint = SvgPaint(aColor, true, false, false);
1103 return true;
1105 else if(readLocalUrl(rCandidate, rURL))
1107 /// Url is copied to rURL, but needs to be solved outside this helper
1108 return false;
1110 else if(rCandidate.startsWith("currentColor"))
1112 rSvgPaint = SvgPaint(aColor, true, true, true);
1113 return true;
1118 return false;
1121 bool readSvgNumberVector(const OUString& rCandidate, SvgNumberVector& rSvgNumberVector)
1123 const sal_Int32 nLen(rCandidate.getLength());
1124 rSvgNumberVector.clear();
1126 if(nLen)
1128 sal_Int32 nPos(0);
1129 SvgNumber aNum;
1130 skip_char(rCandidate, ' ', ',', nPos, nLen);
1132 while(readNumberAndUnit(rCandidate, nPos, aNum, nLen))
1134 rSvgNumberVector.push_back(aNum);
1135 skip_char(rCandidate, ' ', ',', nPos, nLen);
1138 return !rSvgNumberVector.empty();
1141 return false;
1144 SvgAspectRatio readSvgAspectRatio(const OUString& rCandidate)
1146 const sal_Int32 nLen(rCandidate.getLength());
1148 if(nLen)
1150 sal_Int32 nPos(0);
1151 SvgAlign aSvgAlign(SvgAlign::xMidYMid);
1152 bool bMeetOrSlice(true);
1153 bool bChanged(false);
1155 while(nPos < nLen)
1157 const sal_Int32 nInitPos(nPos);
1158 skip_char(rCandidate, ' ', nPos, nLen);
1159 OUStringBuffer aTokenName;
1160 copyString(rCandidate, nPos, aTokenName, nLen);
1162 if(!aTokenName.isEmpty())
1164 switch(StrToSVGToken(aTokenName.makeStringAndClear(), false))
1166 case SVGToken::Defer:
1168 bChanged = true;
1169 break;
1171 case SVGToken::None:
1173 aSvgAlign = SvgAlign::none;
1174 bChanged = true;
1175 break;
1177 case SVGToken::XMinYMin:
1179 aSvgAlign = SvgAlign::xMinYMin;
1180 bChanged = true;
1181 break;
1183 case SVGToken::XMidYMin:
1185 aSvgAlign = SvgAlign::xMidYMin;
1186 bChanged = true;
1187 break;
1189 case SVGToken::XMaxYMin:
1191 aSvgAlign = SvgAlign::xMaxYMin;
1192 bChanged = true;
1193 break;
1195 case SVGToken::XMinYMid:
1197 aSvgAlign = SvgAlign::xMinYMid;
1198 bChanged = true;
1199 break;
1201 case SVGToken::XMidYMid:
1203 aSvgAlign = SvgAlign::xMidYMid;
1204 bChanged = true;
1205 break;
1207 case SVGToken::XMaxYMid:
1209 aSvgAlign = SvgAlign::xMaxYMid;
1210 bChanged = true;
1211 break;
1213 case SVGToken::XMinYMax:
1215 aSvgAlign = SvgAlign::xMinYMax;
1216 bChanged = true;
1217 break;
1219 case SVGToken::XMidYMax:
1221 aSvgAlign = SvgAlign::xMidYMax;
1222 bChanged = true;
1223 break;
1225 case SVGToken::XMaxYMax:
1227 aSvgAlign = SvgAlign::xMaxYMax;
1228 bChanged = true;
1229 break;
1231 case SVGToken::Meet:
1233 bMeetOrSlice = true;
1234 bChanged = true;
1235 break;
1237 case SVGToken::Slice:
1239 bMeetOrSlice = false;
1240 bChanged = true;
1241 break;
1243 default:
1245 break;
1250 if(nInitPos == nPos)
1252 SAL_WARN("svgio", "Could not interpret on current position (!)");
1253 nPos++;
1257 if(bChanged)
1259 return SvgAspectRatio(aSvgAlign, bMeetOrSlice);
1263 return SvgAspectRatio();
1266 bool readSvgStringVector(const OUString& rCandidate, SvgStringVector& rSvgStringVector)
1268 rSvgStringVector.clear();
1269 const sal_Int32 nLen(rCandidate.getLength());
1271 if(nLen)
1273 sal_Int32 nPos(0);
1274 OUStringBuffer aTokenValue;
1275 skip_char(rCandidate, ' ', ',', nPos, nLen);
1277 while(nPos < nLen)
1279 copyToLimiter(rCandidate, ',', nPos, aTokenValue, nLen);
1280 skip_char(rCandidate, ',', ' ', nPos, nLen);
1281 const OUString aString = aTokenValue.makeStringAndClear();
1283 if(!aString.isEmpty())
1285 rSvgStringVector.push_back(aString);
1290 return !rSvgStringVector.empty();
1293 void readImageLink(const OUString& rCandidate, OUString& rXLink, OUString& rUrl, OUString& rMimeType, OUString& rData)
1295 rXLink.clear();
1296 rUrl.clear();
1297 rMimeType.clear();
1298 rData.clear();
1300 if('#' == rCandidate[0])
1302 // local link
1303 rXLink = rCandidate.copy(1);
1305 else
1307 static const char aStrData[] = "data:";
1309 if(rCandidate.match(aStrData, 0))
1311 // embedded data
1312 sal_Int32 nPos(strlen(aStrData));
1313 sal_Int32 nLen(rCandidate.getLength());
1314 OUStringBuffer aBuffer;
1316 // read mime type
1317 skip_char(rCandidate, ' ', nPos, nLen);
1318 copyToLimiter(rCandidate, ';', nPos, aBuffer, nLen);
1319 skip_char(rCandidate, ' ', ';', nPos, nLen);
1320 rMimeType = aBuffer.makeStringAndClear();
1322 if(!rMimeType.isEmpty() && nPos < nLen)
1324 if(rMimeType.startsWith("image"))
1326 // image data
1327 OUString aData(rCandidate.copy(nPos));
1328 static const char aStrBase64[] = "base64";
1330 if(aData.startsWith(aStrBase64))
1332 // base64 encoded
1333 nPos = strlen(aStrBase64);
1334 nLen = aData.getLength();
1336 skip_char(aData, ' ', ',', nPos, nLen);
1338 if(nPos < nLen)
1340 rData = aData.copy(nPos);
1346 else
1348 // Url (path and filename)
1349 rUrl = rCandidate;
1354 OUString convert(const OUString& rCandidate, sal_Unicode nPattern, sal_Unicode nNew, bool bRemove)
1356 const sal_Int32 nLen(rCandidate.getLength());
1358 if(nLen)
1360 sal_Int32 nPos(0);
1361 OUStringBuffer aBuffer;
1362 bool bChanged(false);
1364 while(nPos < nLen)
1366 const sal_Unicode aChar(rCandidate[nPos]);
1368 if(nPattern == aChar)
1370 bChanged = true;
1372 if(!bRemove)
1374 aBuffer.append(nNew);
1377 else
1379 aBuffer.append(aChar);
1382 nPos++;
1385 if(bChanged)
1387 return aBuffer.makeStringAndClear();
1391 return rCandidate;
1394 // #i125325#
1395 OUString removeBlockComments(const OUString& rCandidate)
1397 const sal_Int32 nLen(rCandidate.getLength());
1399 if(nLen)
1401 sal_Int32 nPos(0);
1402 OUStringBuffer aBuffer;
1403 bool bChanged(false);
1404 sal_Int32 nInsideComment(0);
1405 const sal_Unicode aCommentSlash('/');
1406 const sal_Unicode aCommentStar('*');
1408 while(nPos < nLen)
1410 const sal_Unicode aChar(rCandidate[nPos]);
1411 const bool bStart(aCommentSlash == aChar && nPos + 1 < nLen && aCommentStar == rCandidate[nPos + 1]);
1412 const bool bEnd(aCommentStar == aChar && nPos + 1 < nLen && aCommentSlash == rCandidate[nPos + 1]);
1414 if(bStart)
1416 nPos += 2;
1417 nInsideComment++;
1418 bChanged = true;
1420 else if(bEnd)
1422 nPos += 2;
1423 nInsideComment--;
1425 else
1427 if(!nInsideComment)
1429 aBuffer.append(aChar);
1432 nPos++;
1436 if(bChanged)
1438 return aBuffer.makeStringAndClear();
1442 return rCandidate;
1445 OUString consolidateContiguousSpace(const OUString& rCandidate)
1447 const sal_Int32 nLen(rCandidate.getLength());
1449 if(nLen)
1451 sal_Int32 nPos(0);
1452 OUStringBuffer aBuffer;
1453 bool bInsideSpace(false);
1454 const sal_Unicode aSpace(' ');
1456 while(nPos < nLen)
1458 const sal_Unicode aChar(rCandidate[nPos]);
1460 if(aSpace == aChar)
1462 bInsideSpace = true;
1464 else
1466 if(bInsideSpace)
1468 bInsideSpace = false;
1469 aBuffer.append(aSpace);
1472 aBuffer.append(aChar);
1475 nPos++;
1478 if(bInsideSpace)
1480 aBuffer.append(aSpace);
1483 if(aBuffer.getLength() != nLen)
1485 return aBuffer.makeStringAndClear();
1489 return rCandidate;
1492 OUString whiteSpaceHandlingDefault(const OUString& rCandidate)
1494 const sal_Unicode aNewline('\n');
1495 const sal_Unicode aTab('\t');
1496 const sal_Unicode aSpace(' ');
1498 // remove all newline characters
1499 OUString aRetval(convert(rCandidate, aNewline, aNewline, true));
1501 // convert tab to space
1502 aRetval = convert(aRetval, aTab, aSpace, false);
1504 // strip of all leading and trailing spaces
1505 aRetval = aRetval.trim();
1507 // consolidate contiguous space
1508 aRetval = consolidateContiguousSpace(aRetval);
1510 return aRetval;
1513 OUString whiteSpaceHandlingPreserve(const OUString& rCandidate)
1515 const sal_Unicode aNewline('\n');
1516 const sal_Unicode aTab('\t');
1517 const sal_Unicode aSpace(' ');
1519 // convert newline to space
1520 convert(rCandidate, aNewline, aSpace, false);
1522 // convert tab to space
1523 convert(rCandidate, aTab, aSpace, false);
1525 return rCandidate;
1528 ::std::vector< double > solveSvgNumberVector(const SvgNumberVector& rInput, const InfoProvider& rInfoProvider)
1530 ::std::vector< double > aRetval;
1532 if(!rInput.empty())
1534 const double nCount(rInput.size());
1535 aRetval.reserve(nCount);
1537 for(sal_uInt32 a(0); a < nCount; a++)
1539 aRetval.push_back(rInput[a].solve(rInfoProvider));
1543 return aRetval;
1546 } // end of namespace svgio::svgreader
1548 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */