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