SVGUtils.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. //
  2. // SVGUtils.m
  3. // SVGKit
  4. //
  5. // Copyright Matt Rajca 2010-2011. All rights reserved.
  6. //
  7. #import "SVGUtils.h"
  8. #import "SVGKDefine_Private.h"
  9. #define MAX_ACCUM 64
  10. #define NUM_COLORS 148
  11. SVGColor ColorValueWithName (const char *name);
  12. static const char *gColorNames[NUM_COLORS] = {
  13. "aliceblue",
  14. "antiquewhite",
  15. "aqua",
  16. "aquamarine",
  17. "azure",
  18. "beige",
  19. "bisque",
  20. "black",
  21. "blanchedalmond",
  22. "blue",
  23. "blueviolet",
  24. "brown",
  25. "burlywood",
  26. "cadetblue",
  27. "chartreuse",
  28. "chocolate",
  29. "coral",
  30. "cornflowerblue",
  31. "cornsilk",
  32. "crimson",
  33. "cyan",
  34. "darkblue",
  35. "darkcyan",
  36. "darkgoldenrod",
  37. "darkgray",
  38. "darkgreen",
  39. "darkgrey",
  40. "darkkhaki",
  41. "darkmagenta",
  42. "darkolivegreen",
  43. "darkorange",
  44. "darkorchid",
  45. "darkred",
  46. "darksalmon",
  47. "darkseagreen",
  48. "darkslateblue",
  49. "darkslategray",
  50. "darkslategrey",
  51. "darkturquoise",
  52. "darkviolet",
  53. "deeppink",
  54. "deepskyblue",
  55. "dimgray",
  56. "dimgrey",
  57. "dodgerblue",
  58. "firebrick",
  59. "floralwhite",
  60. "forestgreen",
  61. "fuchsia",
  62. "gainsboro",
  63. "ghostwhite",
  64. "gold",
  65. "goldenrod",
  66. "gray",
  67. "green",
  68. "greenyellow",
  69. "grey",
  70. "honeydew",
  71. "hotpink",
  72. "indianred",
  73. "indigo",
  74. "ivory",
  75. "khaki",
  76. "lavender",
  77. "lavenderblush",
  78. "lawngreen",
  79. "lemonchiffon",
  80. "lightblue",
  81. "lightcoral",
  82. "lightcyan",
  83. "lightgoldenrodyellow",
  84. "lightgray",
  85. "lightgreen",
  86. "lightgrey",
  87. "lightpink",
  88. "lightsalmon",
  89. "lightseagreen",
  90. "lightskyblue",
  91. "lightslategray",
  92. "lightslategrey",
  93. "lightsteelblue",
  94. "lightyellow",
  95. "lime",
  96. "limegreen",
  97. "linen",
  98. "magenta",
  99. "maroon",
  100. "mediumaquamarine",
  101. "mediumblue",
  102. "mediumorchid",
  103. "mediumpurple",
  104. "mediumseagreen",
  105. "mediumslateblue",
  106. "mediumspringgreen",
  107. "mediumturquoise",
  108. "mediumvioletred",
  109. "midnightblue",
  110. "mintcream",
  111. "mistyrose",
  112. "moccasin",
  113. "navajowhite",
  114. "navy",
  115. "oldlace",
  116. "olive",
  117. "olivedrab",
  118. "orange",
  119. "orangered",
  120. "orchid",
  121. "palegoldenrod",
  122. "palegreen",
  123. "paleturquoise",
  124. "palevioletred",
  125. "papayawhip",
  126. "peachpuff",
  127. "peru",
  128. "pink",
  129. "plum",
  130. "powderblue",
  131. "purple",
  132. "red",
  133. "rosybrown",
  134. "royalblue",
  135. "saddlebrown",
  136. "salmon",
  137. "sandybrown",
  138. "seagreen",
  139. "seashell",
  140. "sienna",
  141. "silver",
  142. "skyblue",
  143. "slateblue",
  144. "slategray",
  145. "slategrey",
  146. "snow",
  147. "springgreen",
  148. "steelblue",
  149. "tan",
  150. "teal",
  151. "thistle",
  152. "tomato",
  153. "turquoise",
  154. "violet",
  155. "wheat",
  156. "white",
  157. "whitesmoke",
  158. "yellow",
  159. "yellowgreen",
  160. // CSS Color
  161. "transparent"
  162. };
  163. static const SVGColor gColorValues[NUM_COLORS] = {
  164. (SVGColor) { 240,248,255,255 }, (SVGColor) { 250,235,215,255 },
  165. (SVGColor) { 0,255,255,255 }, (SVGColor) { 127,255,212,255 },
  166. (SVGColor) { 240,255,255,255 }, (SVGColor) { 245,245,220,255 },
  167. (SVGColor) { 255,228,196,255 }, (SVGColor) { 0,0,0,255 },
  168. (SVGColor) { 255,235,205,255 }, (SVGColor) { 0,0,255,255 },
  169. (SVGColor) { 138,43,226,255 }, (SVGColor) { 165,42,42,255 },
  170. (SVGColor) { 222,184,135,255 }, (SVGColor) { 95,158,160,255 },
  171. (SVGColor) { 127,255,0,255 }, (SVGColor) { 210,105,30,255 },
  172. (SVGColor) { 255,127,80,255 }, (SVGColor) { 100,149,237,255 },
  173. (SVGColor) { 255,248,220,255 }, (SVGColor) { 220,20,60,255 },
  174. (SVGColor) { 0,255,255,255 }, (SVGColor) { 0,0,139,255 },
  175. (SVGColor) { 0,139,139,255 }, (SVGColor) { 184,134,11,255 },
  176. (SVGColor) { 169,169,169,255 }, (SVGColor) { 0,100,0,255 },
  177. (SVGColor) { 169,169,169,255 }, (SVGColor) { 189,183,107,255 },
  178. (SVGColor) { 139,0,139,255 }, (SVGColor) { 85,107,47,255 },
  179. (SVGColor) { 255,140,0,255 }, (SVGColor) { 153,50,204,255 },
  180. (SVGColor) { 139,0,0,255 }, (SVGColor) { 233,150,122,255 },
  181. (SVGColor) { 143,188,143,255 }, (SVGColor) { 72,61,139,255 },
  182. (SVGColor) { 47,79,79,255 }, (SVGColor) { 47,79,79,255 },
  183. (SVGColor) { 0,206,209,255 }, (SVGColor) { 148,0,211,255 },
  184. (SVGColor) { 255,20,147,255 }, (SVGColor) { 0,191,255,255 },
  185. (SVGColor) { 105,105,105,255 }, (SVGColor) { 105,105,105,255 },
  186. (SVGColor) { 30,144,255,255 }, (SVGColor) { 178,34,34,255 },
  187. (SVGColor) { 255,250,240,255 }, (SVGColor) { 34,139,34,255 },
  188. (SVGColor) { 255,0,255,255 }, (SVGColor) { 220,220,220,255 },
  189. (SVGColor) { 248,248,255,255 }, (SVGColor) { 255,215,0,255 },
  190. (SVGColor) { 218,165,32,255 }, (SVGColor) { 128,128,128,255 },
  191. (SVGColor) { 0,128,0,255 }, (SVGColor) { 173,255,47,255 },
  192. (SVGColor) { 128,128,128,255 }, (SVGColor) { 240,255,240,255 },
  193. (SVGColor) { 255,105,180,255 }, (SVGColor) { 205,92,92,255 },
  194. (SVGColor) { 75,0,130,255 }, (SVGColor) { 255,255,240,255 },
  195. (SVGColor) { 240,230,140,255 }, (SVGColor) { 230,230,250,255 },
  196. (SVGColor) { 255,240,245,255 }, (SVGColor) { 124,252,0,255 },
  197. (SVGColor) { 255,250,205,255 }, (SVGColor) { 173,216,230,255 },
  198. (SVGColor) { 240,128,128,255 }, (SVGColor) { 224,255,255,255 },
  199. (SVGColor) { 250,250,210,255 }, (SVGColor) { 211,211,211,255 },
  200. (SVGColor) { 144,238,144,255 }, (SVGColor) { 211,211,211,255 },
  201. (SVGColor) { 255,182,193,255 }, (SVGColor) { 255,160,122,255 },
  202. (SVGColor) { 32,178,170,255 }, (SVGColor) { 135,206,250,255 },
  203. (SVGColor) { 119,136,153,255 }, (SVGColor) { 119,136,153,255 },
  204. (SVGColor) { 176,196,222,255 }, (SVGColor) { 255,255,224,255 },
  205. (SVGColor) { 0,255,0,255 }, (SVGColor) { 50,205,50,255 },
  206. (SVGColor) { 250,240,230,255 }, (SVGColor) { 255,0,255,255 },
  207. (SVGColor) { 128,0,0,255 }, (SVGColor) { 102,205,170,255 },
  208. (SVGColor) { 0,0,205,255 }, (SVGColor) { 186,85,211,255 },
  209. (SVGColor) { 147,112,219,255 }, (SVGColor) { 60,179,113,255 },
  210. (SVGColor) { 123,104,238,255 }, (SVGColor) { 0,250,154,255 },
  211. (SVGColor) { 72,209,204,255 }, (SVGColor) { 199,21,133,255 },
  212. (SVGColor) { 25,25,112,255 }, (SVGColor) { 245,255,250,255 },
  213. (SVGColor) { 255,228,225,255 }, (SVGColor) { 255,228,181,255 },
  214. (SVGColor) { 255,222,173,255 }, (SVGColor) { 0,0,128,255 },
  215. (SVGColor) { 253,245,230,255 }, (SVGColor) { 128,128,0,255 },
  216. (SVGColor) { 107,142,35,255 }, (SVGColor) { 255,165,0,255 },
  217. (SVGColor) { 255,69,0,255 }, (SVGColor) { 218,112,214,255 },
  218. (SVGColor) { 238,232,170,255 }, (SVGColor) { 152,251,152,255 },
  219. (SVGColor) { 175,238,238,255 }, (SVGColor) { 219,112,147,255 },
  220. (SVGColor) { 255,239,213,255 }, (SVGColor) { 255,218,185,255 },
  221. (SVGColor) { 205,133,63,255 }, (SVGColor) { 255,192,203,255 },
  222. (SVGColor) { 221,160,221,255 }, (SVGColor) { 176,224,230,255 },
  223. (SVGColor) { 128,0,128,255 }, (SVGColor) { 255,0,0,255 },
  224. (SVGColor) { 188,143,143,255 }, (SVGColor) { 65,105,225,255 },
  225. (SVGColor) { 139,69,19,255 }, (SVGColor) { 250,128,114,255 },
  226. (SVGColor) { 244,164,96,255 }, (SVGColor) { 46,139,87,255 },
  227. (SVGColor) { 255,245,238,255 }, (SVGColor) { 160,82,45,255 },
  228. (SVGColor) { 192,192,192,255 }, (SVGColor) { 135,206,235,255 },
  229. (SVGColor) { 106,90,205,255 }, (SVGColor) { 112,128,144,255 },
  230. (SVGColor) { 112,128,144,255 }, (SVGColor) { 255,250,250,255 },
  231. (SVGColor) { 0,255,127,255 }, (SVGColor) { 70,130,180,255 },
  232. (SVGColor) { 210,180,140,255 }, (SVGColor) { 0,128,128,255 },
  233. (SVGColor) { 216,191,216,255 }, (SVGColor) { 255,99,71,255 },
  234. (SVGColor) { 64,224,208,255 }, (SVGColor) { 238,130,238,255 },
  235. (SVGColor) { 245,222,179,255 }, (SVGColor) { 255,255,255,255 },
  236. (SVGColor) { 245,245,245,255 }, (SVGColor) { 255,255,0,255 },
  237. (SVGColor) { 154,205,50,255 },
  238. // CSS Color
  239. (SVGColor) { 0, 0, 0, 0}
  240. };
  241. SVGColor ColorValueWithName (const char *name) {
  242. int idx = -1;
  243. for (int n = 0; n < NUM_COLORS; n++) {
  244. if (!strcmp(gColorNames[n], name)) {
  245. idx = n;
  246. break;
  247. }
  248. }
  249. if (idx == -1) {
  250. return SVGColorMake(0, 0, 0, 255); // black
  251. }
  252. return gColorValues[idx];
  253. }
  254. SVGColor SVGColorMake (uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
  255. SVGColor color = { .r = r, .g = g, .b = b, .a = a };
  256. return color;
  257. }
  258. typedef enum {
  259. PhaseNone = 0,
  260. PhaseRGB,
  261. PhaseRGBA,
  262. PhaseHSL,
  263. PhaseHSLA
  264. } Phase;
  265. SVGColor SVGColorFromString (const char *string) {
  266. NSCAssert(string != NULL, @"NullPointerException: you gave us a null pointer, very bad thing to do...");
  267. SVGColor color;
  268. bzero(&color, sizeof(color));
  269. color.a = 0xFF;
  270. if (!strncmp(string, "rgb(", 4) || !strncmp(string, "rgba(", 5)) {
  271. size_t len = strlen(string);
  272. char accum[MAX_ACCUM];
  273. bzero(accum, MAX_ACCUM);
  274. int accumIdx = 0, currComponent = 0;
  275. Phase phase = PhaseNone;
  276. for (size_t n = 0; n < len; n++) {
  277. char c = string[n];
  278. if (c == '\n' || c == '\t' || c == ' ') {
  279. continue;
  280. }
  281. if (!strcmp(accum, "rgba(")) {
  282. phase = PhaseRGBA;
  283. bzero(accum, MAX_ACCUM);
  284. accumIdx = 0;
  285. } else if (!strcmp(accum, "rgb(")) {
  286. phase = PhaseRGB;
  287. bzero(accum, MAX_ACCUM);
  288. accumIdx = 0;
  289. }
  290. if (phase == PhaseRGB || phase == PhaseRGBA) {
  291. if (c == ',') {
  292. if (currComponent == 0) {
  293. color.r = atoi(accum);
  294. currComponent++;
  295. }
  296. else if (currComponent == 1) {
  297. color.g = atoi(accum);
  298. currComponent++;
  299. }
  300. else if (phase == PhaseRGBA && currComponent == 2) {
  301. color.b = atoi(accum);
  302. currComponent++;
  303. }
  304. bzero(accum, MAX_ACCUM);
  305. accumIdx = 0;
  306. continue;
  307. }
  308. else if (c == ')' && currComponent == 2) {
  309. color.b = atoi(accum);
  310. break;
  311. }
  312. else if (c == ')' && currComponent == 3) {
  313. color.a = (uint8_t)lround(atof(accum) * 255.0f);
  314. break;
  315. }
  316. }
  317. accum[accumIdx++] = c;
  318. }
  319. }
  320. else if (!strncmp(string, "hsl(", 4) || !strncmp(string, "hsla(", 5)) {
  321. CGFloat h; CGFloat s; CGFloat l;
  322. size_t len = strlen(string);
  323. char accum[MAX_ACCUM];
  324. bzero(accum, MAX_ACCUM);
  325. int accumIdx = 0, currComponent = 0;
  326. Phase phase = PhaseNone;
  327. for (size_t n = 0; n < len; n++) {
  328. char c = string[n];
  329. if (c == '\n' || c == '\t' || c == ' ') {
  330. continue;
  331. }
  332. if (!strcmp(accum, "hsla(")) {
  333. phase = PhaseHSLA;
  334. bzero(accum, MAX_ACCUM);
  335. accumIdx = 0;
  336. } else if (!strcmp(accum, "hsl(")) {
  337. phase = PhaseHSL;
  338. bzero(accum, MAX_ACCUM);
  339. accumIdx = 0;
  340. }
  341. if (phase == PhaseHSL || phase == PhaseHSLA) {
  342. if (c == ',') {
  343. if (currComponent == 0) {
  344. h = atof(accum) / 360.0f;
  345. currComponent++;
  346. }
  347. else if (currComponent == 1) {
  348. s = SVGPercentageFromString(accum);
  349. currComponent++;
  350. }
  351. else if (phase == PhaseHSLA && currComponent == 2) {
  352. l = SVGPercentageFromString(accum);
  353. currComponent++;
  354. }
  355. bzero(accum, MAX_ACCUM);
  356. accumIdx = 0;
  357. continue;
  358. }
  359. else if (c == ')' && currComponent == 2) {
  360. l = SVGPercentageFromString(accum);
  361. break;
  362. }
  363. else if (c == ')' && currComponent == 3) {
  364. if (atof(accum) > 1.0f) {
  365. color.a = 255.0f;
  366. } else {
  367. color.a = (uint8_t)lround(atof(accum) * 255.0f);
  368. }
  369. break;
  370. }
  371. }
  372. accum[accumIdx++] = c;
  373. }
  374. // hsla to rbg
  375. if(s == 0.0f){
  376. color.r = color.g = color.b = l; // achromatic
  377. }else{
  378. CGFloat q; CGFloat p;
  379. q = l < 0.5f ? l * (1.0f + s) : l + s - l * s;
  380. p = 2.0f * l - q;
  381. color.r = (uint8_t)lround(SVGHSLColorToRGB(p, q, h + 0.33f) * 255.0f);
  382. color.g = (uint8_t)lround(SVGHSLColorToRGB(p, q, h) * 255.0f);
  383. color.b = (uint8_t)lround(SVGHSLColorToRGB(p, q, h - 0.33f) * 255.0f);
  384. }
  385. }
  386. else if (!strncmp(string, "#", 1)) {
  387. const char *hexString = string + 1;
  388. if (strlen(hexString) == 6)
  389. {
  390. char r[3], g[3], b[3];
  391. r[2] = g[2] = b[2] = '\0';
  392. strncpy(r, hexString, 2);
  393. strncpy(g, hexString + 2, 2);
  394. strncpy(b, hexString + 4, 2);
  395. color.r = strtol(r, NULL, 16);
  396. color.g = strtol(g, NULL, 16);
  397. color.b = strtol(b, NULL, 16);
  398. }
  399. else if( strlen(hexString) == 3 )
  400. {
  401. char r[2], g[2], b[2];
  402. r[1] = g[1] = b[1] = '\0';
  403. strncpy(r, hexString, 1);
  404. strncpy(g, hexString + 1, 1);
  405. strncpy(b, hexString + 2, 1);
  406. color.r = strtol(r, NULL, 16);
  407. color.g = strtol(g, NULL, 16);
  408. color.b = strtol(b, NULL, 16);
  409. /** because 3-digit hex notation "F" means "FF" ... "1" means "11" ... etc */
  410. color.r += color.r * 16;
  411. color.g += color.g * 16;
  412. color.b += color.b * 16;
  413. }
  414. else
  415. {
  416. color = SVGColorMake(0, 0, 0, 255);
  417. }
  418. }
  419. else {
  420. color = ColorValueWithName(string);
  421. }
  422. return color;
  423. }
  424. CGFloat SVGHSLColorToRGB (CGFloat p, CGFloat q, CGFloat t) {
  425. if(t < 0.0f) t += 1.0f;
  426. if(t > 1.0f) t -= 1.0f;
  427. if(t < 0.166f) return p + (q - p) * 6.0f * t;
  428. if(t < 0.5f) return q;
  429. if(t < 0.66f) return p + (q - p) * (0.66f - t) * 6.0f;
  430. return p;
  431. }
  432. CGFloat SVGPercentageFromString (const char *string) {
  433. size_t len = strlen(string);
  434. if (string[len-1] != '%') {
  435. SVGKitLogWarn(@"Invalid percentage: %s", string);
  436. return -1;
  437. }
  438. return atoi(string) / 100.0f;
  439. }
  440. CGMutablePathRef createPathFromPointsInString (const char *string, boolean_t close) {
  441. CGMutablePathRef path = CGPathCreateMutable();
  442. size_t len = strlen(string);
  443. char accum[MAX_ACCUM];
  444. bzero(accum, MAX_ACCUM);
  445. int accumIdx = 0, currComponent = 0;
  446. for (size_t n = 0; n <= len; n++) {
  447. char c = string[n];
  448. if (c == '\n' || c == '\t' || c == ' ' || c == ',' || c == '\0') {
  449. accum[accumIdx] = '\0';
  450. static float x, y;
  451. if (currComponent == 0 && accumIdx != 0) {
  452. sscanf( accum, "%g", &x );
  453. currComponent++;
  454. }
  455. else if (currComponent == 1) {
  456. sscanf( accum, "%g", &y );
  457. if (CGPathIsEmpty(path)) {
  458. CGPathMoveToPoint(path, NULL, x, y);
  459. }
  460. else {
  461. CGPathAddLineToPoint(path, NULL, x, y);
  462. }
  463. currComponent = 0;
  464. }
  465. bzero(accum, MAX_ACCUM);
  466. accumIdx = 0;
  467. }
  468. else if (isdigit(c) || c == '-' || c == '.') { // is digit or decimal separator OR A MINUS SIGN!!! ?
  469. accum[accumIdx++] = c;
  470. }
  471. }
  472. if (close) {
  473. CGPathCloseSubpath(path);
  474. }
  475. return path;
  476. }
  477. CGColorRef CGColorWithSVGColor (SVGColor color) {
  478. CGColorRef outColor = NULL;
  479. outColor = [UIColor colorWithRed:RGB_N(color.r)
  480. green:RGB_N(color.g)
  481. blue:RGB_N(color.b)
  482. alpha:RGB_N(color.a)].CGColor;
  483. return outColor;
  484. }