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