static fz_path * svg_parse_polygon_imp(fz_context *ctx, svg_document *doc, fz_xml *node, int doclose) { fz_path *path; const char *str = fz_xml_att(node, "points"); float number; float args[2]; int nargs; int isfirst; if (!str) return NULL; isfirst = 1; nargs = 0; path = fz_new_path(ctx); while (*str) { while (svg_is_whitespace_or_comma(*str)) str ++; if (svg_is_digit(*str)) { str = svg_lex_number(&number, str); args[nargs++] = number; } if (nargs == 2) { if (isfirst) { fz_moveto(ctx, path, args[0], args[1]); isfirst = 0; } else { fz_lineto(ctx, path, args[0], args[1]); } nargs = 0; } } return path; }
void svg_parse_color(fz_context *ctx, svg_document *doc, const char *str, float *rgb) { int i, l, m, r, cmp, n; rgb[0] = 0.0f; rgb[1] = 0.0f; rgb[2] = 0.0f; /* Crack hex-coded RGB */ if (str[0] == '#') { str ++; n = strlen(str); if (n == 3 || (n > 3 && !ishex(str[3]))) { rgb[0] = (unhex(str[0]) * 16 + unhex(str[0])) / 255.0f; rgb[1] = (unhex(str[1]) * 16 + unhex(str[1])) / 255.0f; rgb[2] = (unhex(str[2]) * 16 + unhex(str[2])) / 255.0f; return; } if (n >= 6) { rgb[0] = (unhex(str[0]) * 16 + unhex(str[1])) / 255.0f; rgb[1] = (unhex(str[2]) * 16 + unhex(str[3])) / 255.0f; rgb[2] = (unhex(str[4]) * 16 + unhex(str[5])) / 255.0f; return; } return; } /* rgb(X,Y,Z) -- whitespace allowed around numbers */ else if (strstr(str, "rgb(")) { int numberlen = 0; char numberbuf[50]; str = str + 4; for (i = 0; i < 3; i++) { while (svg_is_whitespace_or_comma(*str)) str ++; if (svg_is_digit(*str)) { numberlen = 0; while (svg_is_digit(*str) && numberlen < sizeof(numberbuf) - 1) numberbuf[numberlen++] = *str++; numberbuf[numberlen] = 0; if (*str == '%') { str ++; rgb[i] = fz_atof(numberbuf) / 100.0f; } else { rgb[i] = fz_atof(numberbuf) / 255.0f; } } } return; } /* TODO: parse icc-profile(X,Y,Z,W) syntax */ /* Search for a pre-defined color */ else { char keyword[50], *p; fz_strlcpy(keyword, str, sizeof keyword); p = keyword; while (*p && *p >= 'a' && *p <= 'z') ++p; *p = 0; l = 0; r = sizeof(svg_predefined_colors) / sizeof(svg_predefined_colors[0]); while (l <= r) { m = (l + r) / 2; cmp = strcmp(svg_predefined_colors[m].name, keyword); if (cmp > 0) r = m - 1; else if (cmp < 0) l = m + 1; else { rgb[0] = svg_predefined_colors[m].red / 255.0f; rgb[1] = svg_predefined_colors[m].green / 255.0f; rgb[2] = svg_predefined_colors[m].blue / 255.0f; return; } } } }
static fz_path * svg_parse_path_data(fz_context *ctx, svg_document *doc, const char *str) { fz_path *path = fz_new_path(ctx); fz_point p; float x1, y1, x2, y2; int cmd; float number; float args[6]; int nargs; /* saved control point for smooth curves */ int reset_smooth = 1; float smooth_x = 0.0; float smooth_y = 0.0; cmd = 0; nargs = 0; fz_try(ctx) { fz_moveto(ctx, path, 0.0, 0.0); /* for the case of opening 'm' */ while (*str) { while (svg_is_whitespace_or_comma(*str)) str ++; if (svg_is_digit(*str)) { str = svg_lex_number(&number, str); if (nargs == 6) fz_throw(ctx, FZ_ERROR_GENERIC, "stack overflow in path data"); args[nargs++] = number; } else if (svg_is_alpha(*str)) { if (nargs != 0) fz_throw(ctx, FZ_ERROR_GENERIC, "syntax error in path data (wrong number of parameters to '%c')", cmd); cmd = *str++; } else if (*str == 0) { break; } else { fz_throw(ctx, FZ_ERROR_GENERIC, "syntax error in path data: '%c'", *str); } if (reset_smooth) { smooth_x = 0.0; smooth_y = 0.0; } reset_smooth = 1; switch (cmd) { case 'M': if (nargs == 2) { fz_moveto(ctx, path, args[0], args[1]); nargs = 0; cmd = 'L'; /* implicit lineto after */ } break; case 'm': if (nargs == 2) { p = fz_currentpoint(ctx, path); fz_moveto(ctx, path, p.x + args[0], p.y + args[1]); nargs = 0; cmd = 'l'; /* implicit lineto after */ } break; case 'Z': case 'z': if (nargs == 0) { fz_closepath(ctx, path); } break; case 'L': if (nargs == 2) { fz_lineto(ctx, path, args[0], args[1]); nargs = 0; } break; case 'l': if (nargs == 2) { p = fz_currentpoint(ctx, path); fz_lineto(ctx, path, p.x + args[0], p.y + args[1]); nargs = 0; } break; case 'H': if (nargs == 1) { p = fz_currentpoint(ctx, path); fz_lineto(ctx, path, args[0], p.y); nargs = 0; } break; case 'h': if (nargs == 1) { p = fz_currentpoint(ctx, path); fz_lineto(ctx, path, p.x + args[0], p.y); nargs = 0; } break; case 'V': if (nargs == 1) { p = fz_currentpoint(ctx, path); fz_lineto(ctx, path, p.x, args[0]); nargs = 0; } break; case 'v': if (nargs == 1) { p = fz_currentpoint(ctx, path); fz_lineto(ctx, path, p.x, p.y + args[0]); nargs = 0; } break; case 'C': reset_smooth = 0; if (nargs == 6) { fz_curveto(ctx, path, args[0], args[1], args[2], args[3], args[4], args[5]); smooth_x = args[4] - args[2]; smooth_y = args[5] - args[3]; nargs = 0; } break; case 'c': reset_smooth = 0; if (nargs == 6) { p = fz_currentpoint(ctx, path); fz_curveto(ctx, path, p.x + args[0], p.y + args[1], p.x + args[2], p.y + args[3], p.x + args[4], p.y + args[5]); smooth_x = args[4] - args[2]; smooth_y = args[5] - args[3]; nargs = 0; } break; case 'S': reset_smooth = 0; if (nargs == 4) { p = fz_currentpoint(ctx, path); fz_curveto(ctx, path, p.x + smooth_x, p.y + smooth_y, args[0], args[1], args[2], args[3]); smooth_x = args[2] - args[0]; smooth_y = args[3] - args[1]; nargs = 0; } break; case 's': reset_smooth = 0; if (nargs == 4) { p = fz_currentpoint(ctx, path); fz_curveto(ctx, path, p.x + smooth_x, p.y + smooth_y, p.x + args[0], p.y + args[1], p.x + args[2], p.y + args[3]); smooth_x = args[2] - args[0]; smooth_y = args[3] - args[1]; nargs = 0; } break; case 'Q': reset_smooth = 0; if (nargs == 4) { p = fz_currentpoint(ctx, path); x1 = args[0]; y1 = args[1]; x2 = args[2]; y2 = args[3]; fz_curveto(ctx, path, (p.x + 2 * x1) / 3, (p.y + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); smooth_x = x2 - x1; smooth_y = y2 - y1; nargs = 0; } break; case 'q': reset_smooth = 0; if (nargs == 4) { p = fz_currentpoint(ctx, path); x1 = args[0] + p.x; y1 = args[1] + p.y; x2 = args[2] + p.x; y2 = args[3] + p.y; fz_curveto(ctx, path, (p.x + 2 * x1) / 3, (p.y + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); smooth_x = x2 - x1; smooth_y = y2 - y1; nargs = 0; } break; case 'T': reset_smooth = 0; if (nargs == 4) { p = fz_currentpoint(ctx, path); x1 = p.x + smooth_x; y1 = p.y + smooth_y; x2 = args[0]; y2 = args[1]; fz_curveto(ctx, path, (p.x + 2 * x1) / 3, (p.y + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); smooth_x = x2 - x1; smooth_y = y2 - y1; nargs = 0; } break; case 't': reset_smooth = 0; if (nargs == 4) { p = fz_currentpoint(ctx, path); x1 = p.x + smooth_x; y1 = p.y + smooth_y; x2 = args[0] + p.x; y2 = args[1] + p.y; fz_curveto(ctx, path, (p.x + 2 * x1) / 3, (p.y + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); smooth_x = x2 - x1; smooth_y = y2 - y1; nargs = 0; } break; case 0: if (nargs != 0) fz_throw(ctx, FZ_ERROR_GENERIC, "path data must begin with a command"); break; default: fz_throw(ctx, FZ_ERROR_GENERIC, "unrecognized command in path data: '%c'", cmd); } } } fz_catch(ctx) { fz_drop_path(ctx, path); fz_rethrow(ctx); } return path; }
/* Coordinate transformations */ fz_matrix svg_parse_transform(fz_context *ctx, svg_document *doc, const char *str, fz_matrix transform) { char keyword[20]; int keywordlen; float args[6]; int nargs; nargs = 0; keywordlen = 0; while (*str) { while (svg_is_whitespace_or_comma(*str)) str ++; if (*str == 0) break; /* * Parse keyword and opening parenthesis. */ keywordlen = 0; while (svg_is_alpha(*str) && keywordlen < sizeof(keyword) - 1) keyword[keywordlen++] = *str++; keyword[keywordlen] = 0; if (keywordlen == 0) fz_throw(ctx, FZ_ERROR_SYNTAX, "expected keyword in transform attribute"); while (svg_is_whitespace(*str)) str ++; if (*str != '(') fz_throw(ctx, FZ_ERROR_SYNTAX, "expected opening parenthesis in transform attribute"); str ++; /* * Parse list of numbers until closing parenthesis */ nargs = 0; while (*str && *str != ')' && nargs < 6) { while (svg_is_whitespace_or_comma(*str)) str ++; if (svg_is_digit(*str)) str = svg_lex_number(&args[nargs++], str); } if (*str != ')') fz_throw(ctx, FZ_ERROR_SYNTAX, "expected closing parenthesis in transform attribute"); str ++; /* * Execute the transform. */ if (!strcmp(keyword, "matrix")) { fz_matrix m; if (nargs != 6) fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to matrix(): %d", nargs); m.a = args[0]; m.b = args[1]; m.c = args[2]; m.d = args[3]; m.e = args[4]; m.f = args[5]; transform = fz_concat(transform, m); } else if (!strcmp(keyword, "translate")) { if (nargs != 2) fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to translate(): %d", nargs); transform = fz_pre_translate(transform, args[0], args[1]); } else if (!strcmp(keyword, "scale")) { if (nargs == 1) transform = fz_pre_scale(transform, args[0], args[0]); else if (nargs == 2) transform = fz_pre_scale(transform, args[0], args[1]); else fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to scale(): %d", nargs); } else if (!strcmp(keyword, "rotate")) { if (nargs != 1) fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to rotate(): %d", nargs); transform = fz_pre_rotate(transform, args[0]); } else if (!strcmp(keyword, "skewX")) { fz_matrix m; if (nargs != 1) fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to skewX(): %d", nargs); m.a = 1; m.b = 0; m.c = tanf(args[0] * FZ_DEGREE); m.d = 1; m.e = 0; m.f = 0; transform = fz_concat(transform, m); } else if (!strcmp(keyword, "skewY")) { fz_matrix m; if (nargs != 1) fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to skewY(): %d", nargs); m.a = 1; m.b = tanf(args[0] * FZ_DEGREE); m.c = 0; m.d = 1; m.e = 0; m.f = 0; transform = fz_concat(transform, m); } else { fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown transform function: %s", keyword); } } return transform; }