1 | #include "mupdf/fitz.h" |
2 | #include "svg-imp.h" |
3 | |
4 | #include <string.h> |
5 | |
6 | /* Color keywords (white, blue, fuchsia) |
7 | * System color keywords (ActiveBorder, ButtonFace -- need to find reasonable defaults) |
8 | * #fb0 (expand to #ffbb00) |
9 | * #ffbb00 |
10 | * rgb(255,255,255) |
11 | * rgb(100%,100%,100%) |
12 | * |
13 | * "red icc-color(profileName,255,0,0)" (not going to support for now) |
14 | */ |
15 | |
16 | struct |
17 | { |
18 | const char *name; |
19 | float red, green, blue; |
20 | } |
21 | svg_predefined_colors[] = |
22 | { |
23 | { "aliceblue" , 240, 248, 255 }, |
24 | { "antiquewhite" , 250, 235, 215 }, |
25 | { "aqua" , 0, 255, 255 }, |
26 | { "aquamarine" , 127, 255, 212 }, |
27 | { "azure" , 240, 255, 255 }, |
28 | { "beige" , 245, 245, 220 }, |
29 | { "bisque" , 255, 228, 196 }, |
30 | { "black" , 0, 0, 0 }, |
31 | { "blanchedalmond" , 255, 235, 205 }, |
32 | { "blue" , 0, 0, 255 }, |
33 | { "blueviolet" , 138, 43, 226 }, |
34 | { "brown" , 165, 42, 42 }, |
35 | { "burlywood" , 222, 184, 135 }, |
36 | { "cadetblue" , 95, 158, 160 }, |
37 | { "chartreuse" , 127, 255, 0 }, |
38 | { "chocolate" , 210, 105, 30 }, |
39 | { "coral" , 255, 127, 80 }, |
40 | { "cornflowerblue" , 100, 149, 237 }, |
41 | { "cornsilk" , 255, 248, 220 }, |
42 | { "crimson" , 220, 20, 60 }, |
43 | { "cyan" , 0, 255, 255 }, |
44 | { "darkblue" , 0, 0, 139 }, |
45 | { "darkcyan" , 0, 139, 139 }, |
46 | { "darkgoldenrod" , 184, 134, 11 }, |
47 | { "darkgray" , 169, 169, 169 }, |
48 | { "darkgreen" , 0, 100, 0 }, |
49 | { "darkgrey" , 169, 169, 169 }, |
50 | { "darkkhaki" , 189, 183, 107 }, |
51 | { "darkmagenta" , 139, 0, 139 }, |
52 | { "darkolivegreen" , 85, 107, 47 }, |
53 | { "darkorange" , 255, 140, 0 }, |
54 | { "darkorchid" , 153, 50, 204 }, |
55 | { "darkred" , 139, 0, 0 }, |
56 | { "darksalmon" , 233, 150, 122 }, |
57 | { "darkseagreen" , 143, 188, 143 }, |
58 | { "darkslateblue" , 72, 61, 139 }, |
59 | { "darkslategray" , 47, 79, 79 }, |
60 | { "darkslategrey" , 47, 79, 79 }, |
61 | { "darkturquoise" , 0, 206, 209 }, |
62 | { "darkviolet" , 148, 0, 211 }, |
63 | { "deeppink" , 255, 20, 147 }, |
64 | { "deepskyblue" , 0, 191, 255 }, |
65 | { "dimgray" , 105, 105, 105 }, |
66 | { "dimgrey" , 105, 105, 105 }, |
67 | { "dodgerblue" , 30, 144, 255 }, |
68 | { "firebrick" , 178, 34, 34 }, |
69 | { "floralwhite" , 255, 250, 240 }, |
70 | { "forestgreen" , 34, 139, 34 }, |
71 | { "fuchsia" , 255, 0, 255 }, |
72 | { "gainsboro" , 220, 220, 220 }, |
73 | { "ghostwhite" , 248, 248, 255 }, |
74 | { "gold" , 255, 215, 0 }, |
75 | { "goldenrod" , 218, 165, 32 }, |
76 | { "gray" , 128, 128, 128 }, |
77 | { "green" , 0, 128, 0 }, |
78 | { "greenyellow" , 173, 255, 47 }, |
79 | { "grey" , 128, 128, 128 }, |
80 | { "honeydew" , 240, 255, 240 }, |
81 | { "hotpink" , 255, 105, 180 }, |
82 | { "indianred" , 205, 92, 92 }, |
83 | { "indigo" , 75, 0, 130 }, |
84 | { "ivory" , 255, 255, 240 }, |
85 | { "khaki" , 240, 230, 140 }, |
86 | { "lavender" , 230, 230, 250 }, |
87 | { "lavenderblush" , 255, 240, 245 }, |
88 | { "lawngreen" , 124, 252, 0 }, |
89 | { "lemonchiffon" , 255, 250, 205 }, |
90 | { "lightblue" , 173, 216, 230 }, |
91 | { "lightcoral" , 240, 128, 128 }, |
92 | { "lightcyan" , 224, 255, 255 }, |
93 | { "lightgoldenrodyellow" , 250, 250, 210 }, |
94 | { "lightgray" , 211, 211, 211 }, |
95 | { "lightgreen" , 144, 238, 144 }, |
96 | { "lightgrey" , 211, 211, 211 }, |
97 | { "lightpink" , 255, 182, 193 }, |
98 | { "lightsalmon" , 255, 160, 122 }, |
99 | { "lightseagreen" , 32, 178, 170 }, |
100 | { "lightskyblue" , 135, 206, 250 }, |
101 | { "lightslategray" , 119, 136, 153 }, |
102 | { "lightslategrey" , 119, 136, 153 }, |
103 | { "lightsteelblue" , 176, 196, 222 }, |
104 | { "lightyellow" , 255, 255, 224 }, |
105 | { "lime" , 0, 255, 0 }, |
106 | { "limegreen" , 50, 205, 50 }, |
107 | { "linen" , 250, 240, 230 }, |
108 | { "magenta" , 255, 0, 255 }, |
109 | { "maroon" , 128, 0, 0 }, |
110 | { "mediumaquamarine" , 102, 205, 170 }, |
111 | { "mediumblue" , 0, 0, 205 }, |
112 | { "mediumorchid" , 186, 85, 211 }, |
113 | { "mediumpurple" , 147, 112, 219 }, |
114 | { "mediumseagreen" , 60, 179, 113 }, |
115 | { "mediumslateblue" , 123, 104, 238 }, |
116 | { "mediumspringgreen" , 0, 250, 154 }, |
117 | { "mediumturquoise" , 72, 209, 204 }, |
118 | { "mediumvioletred" , 199, 21, 133 }, |
119 | { "midnightblue" , 25, 25, 112 }, |
120 | { "mintcream" , 245, 255, 250 }, |
121 | { "mistyrose" , 255, 228, 225 }, |
122 | { "moccasin" , 255, 228, 181 }, |
123 | { "navajowhite" , 255, 222, 173 }, |
124 | { "navy" , 0, 0, 128 }, |
125 | { "oldlace" , 253, 245, 230 }, |
126 | { "olive" , 128, 128, 0 }, |
127 | { "olivedrab" , 107, 142, 35 }, |
128 | { "orange" , 255, 165, 0 }, |
129 | { "orangered" , 255, 69, 0 }, |
130 | { "orchid" , 218, 112, 214 }, |
131 | { "palegoldenrod" , 238, 232, 170 }, |
132 | { "palegreen" , 152, 251, 152 }, |
133 | { "paleturquoise" , 175, 238, 238 }, |
134 | { "palevioletred" , 219, 112, 147 }, |
135 | { "papayawhip" , 255, 239, 213 }, |
136 | { "peachpuff" , 255, 218, 185 }, |
137 | { "peru" , 205, 133, 63 }, |
138 | { "pink" , 255, 192, 203 }, |
139 | { "plum" , 221, 160, 221 }, |
140 | { "powderblue" , 176, 224, 230 }, |
141 | { "purple" , 128, 0, 128 }, |
142 | { "red" , 255, 0, 0 }, |
143 | { "rosybrown" , 188, 143, 143 }, |
144 | { "royalblue" , 65, 105, 225 }, |
145 | { "saddlebrown" , 139, 69, 19 }, |
146 | { "salmon" , 250, 128, 114 }, |
147 | { "sandybrown" , 244, 164, 96 }, |
148 | { "seagreen" , 46, 139, 87 }, |
149 | { "seashell" , 255, 245, 238 }, |
150 | { "sienna" , 160, 82, 45 }, |
151 | { "silver" , 192, 192, 192 }, |
152 | { "skyblue" , 135, 206, 235 }, |
153 | { "slateblue" , 106, 90, 205 }, |
154 | { "slategray" , 112, 128, 144 }, |
155 | { "slategrey" , 112, 128, 144 }, |
156 | { "snow" , 255, 250, 250 }, |
157 | { "springgreen" , 0, 255, 127 }, |
158 | { "steelblue" , 70, 130, 180 }, |
159 | { "tan" , 210, 180, 140 }, |
160 | { "teal" , 0, 128, 128 }, |
161 | { "thistle" , 216, 191, 216 }, |
162 | { "tomato" , 255, 99, 71 }, |
163 | { "turquoise" , 64, 224, 208 }, |
164 | { "violet" , 238, 130, 238 }, |
165 | { "wheat" , 245, 222, 179 }, |
166 | { "white" , 255, 255, 255 }, |
167 | { "whitesmoke" , 245, 245, 245 }, |
168 | { "yellow" , 255, 255, 0 }, |
169 | { "yellowgreen" , 154, 205, 50 }, |
170 | }; |
171 | |
172 | static int unhex(int chr) |
173 | { |
174 | const char *hextable = "0123456789abcdef" ; |
175 | return strchr(hextable, (chr|32)) - hextable; |
176 | } |
177 | |
178 | static int ishex(int chr) |
179 | { |
180 | if (chr >= '0' && chr <= '9') return 1; |
181 | if (chr >= 'A' && chr <= 'F') return 1; |
182 | if (chr >= 'a' && chr <= 'f') return 1; |
183 | return 0; |
184 | } |
185 | |
186 | void |
187 | svg_parse_color(fz_context *ctx, svg_document *doc, const char *str, float *rgb) |
188 | { |
189 | int i, l, m, r, cmp, n; |
190 | |
191 | rgb[0] = 0.0f; |
192 | rgb[1] = 0.0f; |
193 | rgb[2] = 0.0f; |
194 | |
195 | /* Crack hex-coded RGB */ |
196 | |
197 | if (str[0] == '#') |
198 | { |
199 | str ++; |
200 | |
201 | n = strlen(str); |
202 | if (n == 3 || (n > 3 && !ishex(str[3]))) |
203 | { |
204 | rgb[0] = (unhex(str[0]) * 16 + unhex(str[0])) / 255.0f; |
205 | rgb[1] = (unhex(str[1]) * 16 + unhex(str[1])) / 255.0f; |
206 | rgb[2] = (unhex(str[2]) * 16 + unhex(str[2])) / 255.0f; |
207 | return; |
208 | } |
209 | |
210 | if (n >= 6) |
211 | { |
212 | rgb[0] = (unhex(str[0]) * 16 + unhex(str[1])) / 255.0f; |
213 | rgb[1] = (unhex(str[2]) * 16 + unhex(str[3])) / 255.0f; |
214 | rgb[2] = (unhex(str[4]) * 16 + unhex(str[5])) / 255.0f; |
215 | return; |
216 | } |
217 | |
218 | return; |
219 | } |
220 | |
221 | /* rgb(X,Y,Z) -- whitespace allowed around numbers */ |
222 | |
223 | else if (strstr(str, "rgb(" )) |
224 | { |
225 | int numberlen = 0; |
226 | char numberbuf[50]; |
227 | |
228 | str = str + 4; |
229 | |
230 | for (i = 0; i < 3; i++) |
231 | { |
232 | while (svg_is_whitespace_or_comma(*str)) |
233 | str ++; |
234 | |
235 | if (svg_is_digit(*str)) |
236 | { |
237 | numberlen = 0; |
238 | while (svg_is_digit(*str) && numberlen < sizeof(numberbuf) - 1) |
239 | numberbuf[numberlen++] = *str++; |
240 | numberbuf[numberlen] = 0; |
241 | |
242 | if (*str == '%') |
243 | { |
244 | str ++; |
245 | rgb[i] = fz_atof(numberbuf) / 100.0f; |
246 | } |
247 | else |
248 | { |
249 | rgb[i] = fz_atof(numberbuf) / 255.0f; |
250 | } |
251 | } |
252 | } |
253 | |
254 | return; |
255 | } |
256 | |
257 | /* TODO: parse icc-profile(X,Y,Z,W) syntax */ |
258 | |
259 | /* Search for a pre-defined color */ |
260 | |
261 | else |
262 | { |
263 | char keyword[50], *p; |
264 | fz_strlcpy(keyword, str, sizeof keyword); |
265 | p = keyword; |
266 | while (*p && *p >= 'a' && *p <= 'z') |
267 | ++p; |
268 | *p = 0; |
269 | |
270 | l = 0; |
271 | r = sizeof(svg_predefined_colors) / sizeof(svg_predefined_colors[0]); |
272 | |
273 | while (l <= r) |
274 | { |
275 | m = (l + r) / 2; |
276 | cmp = strcmp(svg_predefined_colors[m].name, keyword); |
277 | if (cmp > 0) |
278 | r = m - 1; |
279 | else if (cmp < 0) |
280 | l = m + 1; |
281 | else |
282 | { |
283 | rgb[0] = svg_predefined_colors[m].red / 255.0f; |
284 | rgb[1] = svg_predefined_colors[m].green / 255.0f; |
285 | rgb[2] = svg_predefined_colors[m].blue / 255.0f; |
286 | return; |
287 | } |
288 | } |
289 | } |
290 | } |
291 | |
292 | void |
293 | svg_parse_color_from_style(fz_context *ctx, svg_document *doc, const char *str, |
294 | int *fill_is_set, float fill[3], |
295 | int *stroke_is_set, float stroke[3]) |
296 | { |
297 | const char *p; |
298 | |
299 | p = strstr(str, "fill:" ); |
300 | if (p) |
301 | { |
302 | p += 5; |
303 | while (*p && svg_is_whitespace(*p)) |
304 | ++p; |
305 | if (strncmp(p, "none" , 4) != 0) |
306 | { |
307 | svg_parse_color(ctx, doc, p, fill); |
308 | *fill_is_set = 1; |
309 | } |
310 | } |
311 | |
312 | p = strstr(str, "stroke:" ); |
313 | if (p) |
314 | { |
315 | p += 7; |
316 | while (*p && svg_is_whitespace(*p)) |
317 | ++p; |
318 | if (strncmp(p, "none" , 4) != 0) |
319 | { |
320 | svg_parse_color(ctx, doc, p, stroke); |
321 | *stroke_is_set = 1; |
322 | } |
323 | } |
324 | } |
325 | |