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