|
16 | 16 | @import OpenRenderBox; |
17 | 17 | #endif |
18 | 18 |
|
19 | | -// NOTE: Not audited yet. Use with caution. |
20 | 19 | BOOL _CGPathParseString(CGMutablePathRef path, const char *utf8CString) { |
21 | | - if (path == NULL || utf8CString == NULL) { |
22 | | - return NO; |
23 | | - } |
24 | | - |
25 | | - CGFloat numbers[6]; |
| 20 | + double numbers[6]; |
26 | 21 | int numCount = 0; |
27 | 22 | CGFloat currentX = 0.0, currentY = 0.0; |
28 | 23 | CGFloat lastControlX = 0.0, lastControlY = 0.0; |
29 | | - |
30 | 24 | const char *ptr = utf8CString; |
31 | | - |
32 | | - while (YES) { |
33 | | - // Skip whitespace (characters <= 0x1f or space 0x20) |
34 | | - while (*ptr != '\0' && (*ptr <= 0x1f || *ptr == ' ')) { |
35 | | - ptr++; |
36 | | - } |
37 | | - |
38 | | - if (*ptr == '\0') { |
39 | | - // End of string - success if all numbers consumed |
40 | | - return numCount == 0; |
41 | | - } |
42 | | - |
43 | | - char c = *ptr; |
44 | | - |
45 | | - // Check if character can start a number |
46 | | - // Valid: digits 0-9 (0x30-0x39), '-', '+', '.', 'e', 'E', 'p', 'x', "inf" |
47 | | - BOOL isNumberStart = NO; |
48 | | - if ((c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.') { |
49 | | - isNumberStart = YES; |
50 | | - } else if (c == 'e' || c == 'E' || c == 'p' || c == 'x') { |
51 | | - isNumberStart = YES; |
52 | | - } else if (c == 'i') { |
53 | | - // Check for "inf" |
54 | | - if (ptr[1] == 'n' && ptr[2] == 'f') { |
55 | | - isNumberStart = YES; |
56 | | - } |
57 | | - } |
58 | | - |
59 | | - if (isNumberStart) { |
60 | | - if (numCount >= 6) { |
61 | | - return NO; |
| 25 | + do { |
| 26 | + while (*ptr <= 0x1f) { |
| 27 | + switch (*ptr) { |
| 28 | + case 0: return true; |
| 29 | + case 9: case 10: case 12: case 13: ptr++; break; |
| 30 | + default: return false; |
62 | 31 | } |
63 | | - char *endPtr; |
64 | | - numbers[numCount++] = strtod_l(ptr, &endPtr, NULL); |
65 | | - ptr = endPtr; |
66 | | - continue; |
67 | 32 | } |
68 | | - |
69 | | - // Process command character |
| 33 | + unsigned char c = (unsigned char)*ptr; |
| 34 | + BOOL isNumberStart = NO; |
70 | 35 | switch (c) { |
| 36 | + case ' ': break; |
| 37 | + case '+': case '-': case '.': case '0' ... '9': |
| 38 | + case 'E': case 'P': case 'X': case 'e': case 'p': case 'x': |
| 39 | + isNumberStart = YES; |
| 40 | + break; |
| 41 | + case 'I': |
| 42 | + if (ptr[1] == 'n' && ptr[2] == 'f') { isNumberStart = YES; } |
| 43 | + break; |
71 | 44 | case 'm': |
72 | 45 | if (numCount != 2) return NO; |
73 | 46 | CGPathMoveToPoint(path, NULL, numbers[0], numbers[1]); |
74 | | - lastControlX = currentX = numbers[0]; |
75 | | - lastControlY = currentY = numbers[1]; |
| 47 | + currentX = lastControlX = numbers[0]; |
| 48 | + currentY = lastControlY = numbers[1]; |
76 | 49 | numCount = 0; |
77 | 50 | break; |
78 | | - |
79 | 51 | case 'l': |
80 | 52 | if (numCount != 2) return NO; |
81 | 53 | CGPathAddLineToPoint(path, NULL, numbers[0], numbers[1]); |
82 | | - lastControlX = currentX = numbers[0]; |
83 | | - lastControlY = currentY = numbers[1]; |
| 54 | + currentX = lastControlX = numbers[0]; |
| 55 | + currentY = lastControlY = numbers[1]; |
84 | 56 | numCount = 0; |
85 | 57 | break; |
86 | | - |
87 | 58 | case 'c': |
88 | 59 | if (numCount != 6) return NO; |
89 | | - CGPathAddCurveToPoint(path, NULL, |
90 | | - numbers[0], numbers[1], |
91 | | - numbers[2], numbers[3], |
92 | | - numbers[4], numbers[5]); |
93 | | - lastControlX = numbers[2]; |
94 | | - lastControlY = numbers[3]; |
95 | | - currentX = numbers[4]; |
96 | | - currentY = numbers[5]; |
| 60 | + CGPathAddCurveToPoint(path, NULL, numbers[0], numbers[1], |
| 61 | + numbers[2], numbers[3], numbers[4], numbers[5]); |
| 62 | + currentX = numbers[2]; |
| 63 | + currentY = numbers[3]; |
| 64 | + lastControlX = numbers[4]; |
| 65 | + lastControlY = numbers[5]; |
97 | 66 | numCount = 0; |
98 | 67 | break; |
99 | | - |
100 | 68 | case 'q': |
101 | 69 | if (numCount != 4) return NO; |
102 | | - CGPathAddQuadCurveToPoint(path, NULL, |
103 | | - numbers[0], numbers[1], |
| 70 | + CGPathAddQuadCurveToPoint(path, NULL, numbers[0], numbers[1], |
104 | 71 | numbers[2], numbers[3]); |
105 | | - lastControlX = numbers[0]; |
106 | | - lastControlY = numbers[1]; |
107 | | - currentX = numbers[2]; |
108 | | - currentY = numbers[3]; |
| 72 | + currentX = numbers[0]; |
| 73 | + currentY = numbers[1]; |
| 74 | + lastControlX = numbers[2]; |
| 75 | + lastControlY = numbers[3]; |
109 | 76 | numCount = 0; |
110 | 77 | break; |
111 | | - |
112 | 78 | case 't': |
113 | | - // Smooth quad curve: reflect last control point |
114 | 79 | if (numCount != 2) return NO; |
115 | 80 | { |
116 | 81 | CGFloat reflectedX = currentX - 2.0 * lastControlX; |
117 | 82 | CGFloat reflectedY = currentY - 2.0 * lastControlY; |
118 | | - CGPathAddQuadCurveToPoint(path, NULL, |
119 | | - reflectedX, reflectedY, |
| 83 | + CGPathAddQuadCurveToPoint(path, NULL, reflectedX, reflectedY, |
120 | 84 | numbers[0], numbers[1]); |
121 | | - lastControlX = reflectedX; |
122 | | - lastControlY = reflectedY; |
123 | | - currentX = numbers[0]; |
124 | | - currentY = numbers[1]; |
| 85 | + currentX = reflectedX; |
| 86 | + currentY = reflectedY; |
| 87 | + lastControlX = numbers[0]; |
| 88 | + lastControlY = numbers[1]; |
125 | 89 | } |
126 | 90 | numCount = 0; |
127 | 91 | break; |
128 | | - |
129 | 92 | case 'v': |
130 | | - // Smooth cubic curve: use current point as cp1 |
131 | 93 | if (numCount != 4) return NO; |
132 | | - CGPathAddCurveToPoint(path, NULL, |
133 | | - currentX, currentY, |
134 | | - numbers[0], numbers[1], |
135 | | - numbers[2], numbers[3]); |
136 | | - lastControlX = numbers[0]; |
137 | | - lastControlY = numbers[1]; |
138 | | - currentX = numbers[2]; |
139 | | - currentY = numbers[3]; |
| 94 | + CGPathAddCurveToPoint(path, NULL, lastControlX, lastControlY, |
| 95 | + numbers[0], numbers[1], numbers[2], numbers[3]); |
| 96 | + lastControlX = numbers[2]; |
| 97 | + lastControlY = numbers[3]; |
140 | 98 | numCount = 0; |
141 | 99 | break; |
142 | | - |
143 | 100 | case 'y': |
144 | | - // Shorthand cubic: cp2 = endpoint |
145 | 101 | if (numCount != 4) return NO; |
146 | | - CGPathAddCurveToPoint(path, NULL, |
147 | | - numbers[0], numbers[1], |
148 | | - numbers[2], numbers[3], |
149 | | - numbers[2], numbers[3]); |
150 | | - currentX = numbers[2]; |
151 | | - currentY = numbers[3]; |
| 102 | + CGPathAddCurveToPoint(path, NULL, numbers[0], numbers[1], |
| 103 | + numbers[2], numbers[3], numbers[2], numbers[3]); |
| 104 | + lastControlX = numbers[2]; |
| 105 | + lastControlY = numbers[3]; |
152 | 106 | numCount = 0; |
153 | 107 | break; |
154 | | - |
155 | 108 | case 'h': |
156 | 109 | if (numCount != 0) return NO; |
157 | 110 | CGPathCloseSubpath(path); |
| 111 | + lastControlX = 0.0; |
158 | 112 | lastControlY = 0.0; |
159 | 113 | numCount = 0; |
160 | 114 | break; |
161 | | - |
162 | 115 | case 'r': |
163 | | - // Check for "re" (rectangle) |
164 | | - if (ptr[1] == 'e') { |
165 | | - if (numCount != 4) return NO; |
166 | | - CGPathAddRect(path, NULL, CGRectMake(numbers[0], numbers[1], |
167 | | - numbers[2], numbers[3])); |
168 | | - currentX = numbers[0]; |
169 | | - currentY = numbers[1]; |
170 | | - numCount = 0; |
171 | | - ptr++; // Skip 'e' |
172 | | - break; |
173 | | - } |
174 | | - return NO; |
175 | | - |
| 116 | + if (ptr[1] != 'e') return NO; |
| 117 | + if (numCount != 4) return NO; |
| 118 | + CGPathAddRect(path, NULL, CGRectMake(numbers[0], numbers[1], |
| 119 | + numbers[2], numbers[3])); |
| 120 | + ptr++; |
| 121 | + numCount = 0; |
| 122 | + break; |
176 | 123 | default: |
177 | | - return NO; |
| 124 | + return false; |
178 | 125 | } |
179 | | - ptr++; |
180 | | - } |
| 126 | + if (isNumberStart) { |
| 127 | + if (numCount == 6) return false; |
| 128 | + char *endPtr; |
| 129 | + numbers[numCount++] = strtod_l(ptr, &endPtr, NULL); |
| 130 | + ptr = endPtr; |
| 131 | + } else { |
| 132 | + ptr++; |
| 133 | + } |
| 134 | + } while (1); |
181 | 135 | } |
182 | 136 |
|
183 | 137 | typedef struct PathInfo { |
|
0 commit comments