rsvg_parse_transform (cairo_matrix_t *dst, const char *src)
{
    int idx;
    char keyword[32];
    double args[6];
    int n_args;
    guint key_len;
    cairo_matrix_t affine;

    cairo_matrix_init_identity (dst);

    idx = 0;
    while (src[idx]) {
        /* skip initial whitespace */
        while (g_ascii_isspace (src[idx]))
            idx++;

        if (src[idx] == '\0')
            break;

        /* parse keyword */
        for (key_len = 0; key_len < sizeof (keyword); key_len++) {
            char c;

            c = src[idx];
            if (g_ascii_isalpha (c) || c == '-')
                keyword[key_len] = src[idx++];
            else
                break;
        }
        if (key_len >= sizeof (keyword))
            return FALSE;
        keyword[key_len] = '\0';

        /* skip whitespace */
        while (g_ascii_isspace (src[idx]))
            idx++;

        if (src[idx] != '(')
            return FALSE;
        idx++;

        for (n_args = 0;; n_args++) {
            char c;
            char *end_ptr;

            /* skip whitespace */
            while (g_ascii_isspace (src[idx]))
                idx++;
            c = src[idx];
            if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
                if (n_args == sizeof (args) / sizeof (args[0]))
                    return FALSE;       /* too many args */
                args[n_args] = g_ascii_strtod (src + idx, &end_ptr);
                idx = end_ptr - src;

                while (g_ascii_isspace (src[idx]))
                    idx++;

                /* skip optional comma */
                if (src[idx] == ',')
                    idx++;
            } else if (c == ')')
                break;
            else
                return FALSE;
        }
        idx++;

        /* ok, have parsed keyword and args, now modify the transform */
        if (!strcmp (keyword, "matrix")) {
            if (n_args != 6)
                return FALSE;

            cairo_matrix_init (&affine, args[0], args[1], args[2], args[3], args[4], args[5]);
            cairo_matrix_multiply (dst, &affine, dst);
        } else if (!strcmp (keyword, "translate")) {
            if (n_args == 1)
                args[1] = 0;
            else if (n_args != 2)
                return FALSE;
            cairo_matrix_init_translate (&affine, args[0], args[1]);
            cairo_matrix_multiply (dst, &affine, dst);
        } else if (!strcmp (keyword, "scale")) {
            if (n_args == 1)
                args[1] = args[0];
            else if (n_args != 2)
                return FALSE;
            cairo_matrix_init_scale (&affine, args[0], args[1]);
            cairo_matrix_multiply (dst, &affine, dst);
        } else if (!strcmp (keyword, "rotate")) {
            if (n_args == 1) {

                cairo_matrix_init_rotate (&affine, args[0] * M_PI / 180.);
                cairo_matrix_multiply (dst, &affine, dst);
            } else if (n_args == 3) {
                cairo_matrix_init_translate (&affine, args[1], args[2]);
                cairo_matrix_multiply (dst, &affine, dst);

                cairo_matrix_init_rotate (&affine, args[0] * M_PI / 180.);
                cairo_matrix_multiply (dst, &affine, dst);

                cairo_matrix_init_translate (&affine, -args[1], -args[2]);
                cairo_matrix_multiply (dst, &affine, dst);
            } else
                return FALSE;
        } else if (!strcmp (keyword, "skewX")) {
            if (n_args != 1)
                return FALSE;
            _rsvg_cairo_matrix_init_shear (&affine, args[0]);
            cairo_matrix_multiply (dst, &affine, dst);
        } else if (!strcmp (keyword, "skewY")) {
            if (n_args != 1)
                return FALSE;
            _rsvg_cairo_matrix_init_shear (&affine, args[0]);
            /* transpose the affine, given that we know [1] is zero */
            affine.yx = affine.xy;
            affine.xy = 0.;
            cairo_matrix_multiply (dst, &affine, dst);
        } else
            return FALSE;       /* unknown keyword */
    }
    return TRUE;
}