| 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 | |