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