1 | /* |
2 | * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. |
3 | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | * of this software and associated documentation files (the "Software"), to deal |
6 | * in the Software without restriction, including without limitation the rights |
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
8 | * copies of the Software, and to permit persons to whom the Software is |
9 | * furnished to do so, subject to the following conditions: |
10 | |
11 | * The above copyright notice and this permission notice shall be included in all |
12 | * copies or substantial portions of the Software. |
13 | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
20 | * SOFTWARE. |
21 | */ |
22 | |
23 | #include <cstring> |
24 | #include <math.h> |
25 | #include <memory.h> |
26 | #include "tvgSvgUtil.h" |
27 | |
28 | /************************************************************************/ |
29 | /* Internal Class Implementation */ |
30 | /************************************************************************/ |
31 | |
32 | static inline bool _floatExact(float a, float b) |
33 | { |
34 | return memcmp(&a, &b, sizeof(float)) == 0; |
35 | } |
36 | |
37 | static uint8_t _hexCharToDec(const char c) |
38 | { |
39 | if (c >= 'a') return c - 'a' + 10; |
40 | else if (c >= 'A') return c - 'A' + 10; |
41 | else return c - '0'; |
42 | } |
43 | |
44 | static uint8_t _base64Value(const char chr) |
45 | { |
46 | if (chr >= 'A' && chr <= 'Z') return chr - 'A'; |
47 | else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1; |
48 | else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2; |
49 | else if (chr == '+' || chr == '-') return 62; |
50 | else return 63; |
51 | } |
52 | |
53 | |
54 | /************************************************************************/ |
55 | /* External Class Implementation */ |
56 | /************************************************************************/ |
57 | |
58 | |
59 | /* |
60 | * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160 |
61 | * |
62 | * src should be one of the following form : |
63 | * |
64 | * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits] |
65 | * [whitespace] [sign] {INF | INFINITY} |
66 | * [whitespace] [sign] NAN [sequence] |
67 | * |
68 | * No hexadecimal form supported |
69 | * no sequence supported after NAN |
70 | */ |
71 | float svgUtilStrtof(const char *nPtr, char **endPtr) |
72 | { |
73 | if (endPtr) *endPtr = (char*)(nPtr); |
74 | if (!nPtr) return 0.0f; |
75 | |
76 | auto a = nPtr; |
77 | auto iter = nPtr; |
78 | auto val = 0.0f; |
79 | unsigned long long integerPart = 0; |
80 | int minus = 1; |
81 | |
82 | //ignore leading whitespaces |
83 | while (isspace(*iter)) iter++; |
84 | |
85 | //signed or not |
86 | if (*iter == '-') { |
87 | minus = -1; |
88 | iter++; |
89 | } else if (*iter == '+') { |
90 | iter++; |
91 | } |
92 | |
93 | if (tolower(*iter) == 'i') { |
94 | if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3; |
95 | else goto error; |
96 | |
97 | if (tolower(*(iter)) == 'i') { |
98 | if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5; |
99 | else goto error; |
100 | } |
101 | if (endPtr) *endPtr = (char *)(iter); |
102 | return (minus == -1) ? -INFINITY : INFINITY; |
103 | } |
104 | |
105 | if (tolower(*iter) == 'n') { |
106 | if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3; |
107 | else goto error; |
108 | |
109 | if (endPtr) *endPtr = (char *)(iter); |
110 | return (minus == -1) ? -NAN : NAN; |
111 | } |
112 | |
113 | //Optional: integer part before dot |
114 | if (isdigit(*iter)) { |
115 | for (; isdigit(*iter); iter++) { |
116 | integerPart = integerPart * 10ULL + (unsigned long long)(*iter - '0'); |
117 | } |
118 | a = iter; |
119 | } else if (*iter != '.') { |
120 | goto success; |
121 | } |
122 | |
123 | val = static_cast<float>(integerPart); |
124 | |
125 | //Optional: decimal part after dot |
126 | if (*iter == '.') { |
127 | unsigned long long decimalPart = 0; |
128 | unsigned long long pow10 = 1; |
129 | int count = 0; |
130 | |
131 | iter++; |
132 | |
133 | if (isdigit(*iter)) { |
134 | for (; isdigit(*iter); iter++, count++) { |
135 | if (count < 19) { |
136 | decimalPart = decimalPart * 10ULL + + static_cast<unsigned long long>(*iter - '0'); |
137 | pow10 *= 10ULL; |
138 | } |
139 | } |
140 | } else if (isspace(*iter)) { //skip if there is a space after the dot. |
141 | a = iter; |
142 | goto success; |
143 | } |
144 | |
145 | val += static_cast<float>(decimalPart) / static_cast<float>(pow10); |
146 | a = iter; |
147 | } |
148 | |
149 | //Optional: exponent |
150 | if (*iter == 'e' || *iter == 'E') { |
151 | ++iter; |
152 | |
153 | //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em |
154 | if ((*iter == 'm') || (*iter == 'M')) { |
155 | //TODO: We don't support font em unit now, but has to multiply val * font size later... |
156 | a = iter + 1; |
157 | goto success; |
158 | } |
159 | |
160 | //signed or not |
161 | int minus_e = 1; |
162 | |
163 | if (*iter == '-') { |
164 | minus_e = -1; |
165 | ++iter; |
166 | } else if (*iter == '+') { |
167 | iter++; |
168 | } |
169 | |
170 | unsigned int exponentPart = 0; |
171 | |
172 | if (isdigit(*iter)) { |
173 | while (*iter == '0') iter++; |
174 | for (; isdigit(*iter); iter++) { |
175 | exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0'); |
176 | } |
177 | } else if (!isdigit(*(a - 1))) { |
178 | a = nPtr; |
179 | goto success; |
180 | } else if (*iter == 0) { |
181 | goto success; |
182 | } |
183 | |
184 | //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) { |
185 | if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) { |
186 | //val *= 1.0e-308f; |
187 | val *= 1.0e-38f; |
188 | a = iter; |
189 | goto success; |
190 | } |
191 | |
192 | a = iter; |
193 | auto scale = 1.0f; |
194 | |
195 | while (exponentPart >= 8U) { |
196 | scale *= 1E8; |
197 | exponentPart -= 8U; |
198 | } |
199 | while (exponentPart > 0U) { |
200 | scale *= 10.0f; |
201 | exponentPart--; |
202 | } |
203 | val = (minus_e == -1) ? (val / scale) : (val * scale); |
204 | } else if ((iter > nPtr) && !isdigit(*(iter - 1))) { |
205 | a = nPtr; |
206 | goto success; |
207 | } |
208 | |
209 | success: |
210 | if (endPtr) *endPtr = (char*)(a); |
211 | return minus * val; |
212 | |
213 | error: |
214 | if (endPtr) *endPtr = (char*)(nPtr); |
215 | return 0.0f; |
216 | } |
217 | |
218 | |
219 | string svgUtilURLDecode(const char *src) |
220 | { |
221 | if (!src) return nullptr; |
222 | |
223 | auto length = strlen(src); |
224 | if (length == 0) return nullptr; |
225 | |
226 | string decoded; |
227 | decoded.reserve(length); |
228 | |
229 | char a, b; |
230 | while (*src) { |
231 | if (*src == '%' && |
232 | ((a = src[1]) && (b = src[2])) && |
233 | (isxdigit(a) && isxdigit(b))) { |
234 | decoded += (_hexCharToDec(a) << 4) + _hexCharToDec(b); |
235 | src+=3; |
236 | } else if (*src == '+') { |
237 | decoded += ' '; |
238 | src++; |
239 | } else { |
240 | decoded += *src++; |
241 | } |
242 | } |
243 | return decoded; |
244 | } |
245 | |
246 | |
247 | string svgUtilBase64Decode(const char *src) |
248 | { |
249 | if (!src) return nullptr; |
250 | |
251 | auto length = strlen(src); |
252 | if (length == 0) return nullptr; |
253 | |
254 | string decoded; |
255 | decoded.reserve(3*(1+(length >> 2))); |
256 | |
257 | while (*src && *(src+1)) { |
258 | if (*src <= 0x20) { |
259 | ++src; |
260 | continue; |
261 | } |
262 | |
263 | auto value1 = _base64Value(src[0]); |
264 | auto value2 = _base64Value(src[1]); |
265 | decoded += (value1 << 2) + ((value2 & 0x30) >> 4); |
266 | |
267 | if (!src[2] || src[2] == '=' || src[2] == '.') break; |
268 | auto value3 = _base64Value(src[2]); |
269 | decoded += ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2); |
270 | |
271 | if (!src[3] || src[3] == '=' || src[3] == '.') break; |
272 | auto value4 = _base64Value(src[3]); |
273 | decoded += ((value3 & 0x03) << 6) + value4; |
274 | src += 4; |
275 | } |
276 | return decoded; |
277 | } |
278 | |
279 | |
280 | char* svgUtilStrndup(const char* str, size_t n) |
281 | { |
282 | auto len = strlen(str); |
283 | if (len < n) n = len; |
284 | |
285 | auto ret = (char*)malloc(n + 1); |
286 | if (!ret) return nullptr; |
287 | ret[n] = '\0'; |
288 | |
289 | return (char*)memcpy(ret, str, n); |
290 | } |
291 | |