From 9d313b3264a25d9138cbf1b83bd27c2cebdfa343 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 25 Nov 2020 01:03:13 -0500 Subject: [PATCH] path: Special-case rects and circles Write out the commands for rects and circles in a special way, and add code in the parser to recognize this, so we can successfully round-trip these through the SVG path format. The special way - for people who want to use it for debugging - for now is that we use uppercase "Z" to close standard paths, but lowercase "z" to close our special paths. A test is included, but the random path serializations should take care of it, too. --- gsk/gskpath.c | 101 ++++++++++++++++++++++++++++- testsuite/gsk/path-special-cases.c | 31 +++++++++ 2 files changed, 130 insertions(+), 2 deletions(-) diff --git a/gsk/gskpath.c b/gsk/gskpath.c index c065866802..6afe974328 100644 --- a/gsk/gskpath.c +++ b/gsk/gskpath.c @@ -2118,6 +2118,81 @@ parse_command (const char **p, return FALSE; } +static gboolean +parse_string (const char **p, + const char *s) +{ + int len = strlen (s); + if (strncmp (*p, s, len) != 0) + return FALSE; + (*p) += len; + return TRUE; +} + +static gboolean +parse_rectangle (const char **p, + double *x, + double *y, + double *w, + double *h) +{ + const char *o = *p; + double w2; + + /* Check for M%g,%gh%gv%gh%gz without any intervening whitespace */ + if (parse_coordinate_pair (p, x, y) && + parse_string (p, "h") && + parse_coordinate (p, w) && + parse_string (p, "v") && + parse_coordinate (p, h) && + parse_string (p, "h") && + parse_coordinate (p, &w2) && + parse_string (p, "z") && + w2 == - *w) + { + skip_whitespace (p); + + return TRUE; + } + + *p = o; + return FALSE; +} + +static gboolean +parse_circle (const char **p, + double *sx, + double *sy, + double *r) +{ + const char *o = *p; + double r1, r2, r3, mx, my, ex, ey; + + /* Check for M%g,%gA%g,%g,0,1,0,%g,%gA%g,%g,0,1,0,%g,%g + * without any intervening whitespace + */ + if (parse_coordinate_pair (p, sx, sy) && + parse_string (p, "A") && + parse_coordinate_pair (p, r, &r1) && + parse_string (p, "0 0 0") && + parse_coordinate_pair (p, &mx, &my) && + parse_string (p, "A") && + parse_coordinate_pair (p, &r2, &r3) && + parse_string (p, "0 0 0") && + parse_coordinate_pair (p, &ex, &ey) && + parse_string (p, "z") && + *r == r1 && r1 == r2 && r2 == r3 && + *sx == ex && *sy == ey) + { + skip_whitespace (p); + + return TRUE; + } + + *p = o; + return FALSE; +} + /** * gsk_path_parse: * @string: a string @@ -2181,9 +2256,31 @@ gsk_path_parse (const char *string) case 'M': case 'm': { - double x1, y1; + double x1, y1, w, h, r; - if (parse_coordinate_pair (&p, &x1, &y1)) + if (parse_rectangle (&p, &x1, &y1, &w, &h)) + { + gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (x1, y1, w, h)); + if (strchr ("zZX", prev_cmd)) + { + path_x = x1; + path_y = y1; + } + x = x1; + y = y1; + } + else if (parse_circle (&p, &x1, &y1, &r)) + { + gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (x1 - r, y1), r); + if (strchr ("zZX", prev_cmd)) + { + path_x = x1; + path_y = y1; + } + x = x1; + y = y1; + } + else if (parse_coordinate_pair (&p, &x1, &y1)) { if (cmd == 'm') { diff --git a/testsuite/gsk/path-special-cases.c b/testsuite/gsk/path-special-cases.c index 23f08d59d0..04a575ac1f 100644 --- a/testsuite/gsk/path-special-cases.c +++ b/testsuite/gsk/path-special-cases.c @@ -276,6 +276,36 @@ test_rsvg_parse (void) } } +/* Test that circles and rectangles serialize as expected and can be + * round-tripped through strings. + */ +static void +test_serialize_custom_contours (void) +{ + GskPathBuilder *builder; + GskPath *path; + GskPath *path1; + char *string; + char *string1; + + builder = gsk_path_builder_new (); + gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (100, 100), 50); + gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (111, 222, 333, 444)); + path = gsk_path_builder_free_to_path (builder); + + string = gsk_path_to_string (path); + g_assert_cmpstr ("M 150 100 A 50 50 0 0 0 50 100 A 50 50 0 0 0 150 100 z M 111 222 h 333 v 444 h -333 z", ==, string); + + path1 = gsk_path_parse (string); + string1 = gsk_path_to_string (path1); + g_assert_cmpstr (string, ==, string1); + + g_free (string); + g_free (string1); + gsk_path_unref (path); + gsk_path_unref (path1); +} + int main (int argc, char *argv[]) @@ -283,6 +313,7 @@ main (int argc, gtk_test_init (&argc, &argv, NULL); g_test_add_func ("/path/rsvg-parse", test_rsvg_parse); + g_test_add_func ("/path/serialize-custom-contours", test_serialize_custom_contours); return g_test_run (); }