[cairo-commit] 12 commits - configure.ac .gitlab-ci.yml meson.build meson-cc-tests/fuzzer.c src/cairo-analysis-surface.c src/cairo.c src/cairo-ft-font.c src/cairo-ft-private.h src/cairoint.h src/cairo-matrix.c src/cairo-pattern.c src/cairo-pdf-surface.c src/cairo-png.c src/cairo-recording-surface.c src/cairo-svg-glyph-render.c src/cairo-user-font.c src/Makefile.sources src/meson.build test/cairo-logo-font.ttx test/cairo-svg-test-doc.ttx test/cairo-svg-test-fill.ttx test/cairo-svg-test-gradient.ttx test/cairo-svg-test-path.ttx test/cairo-svg-test-shapes.ttx test/cairo-svg-test-stroke.ttx test/cairo-svg-test-transform.ttx test/cairo-test.c test/cairo-test.h test/ft-svg-cairo-logo.c test/ft-svg-color-font.c test/ft-svg-render.c test/Makefile.am test/Makefile.sources test/meson.build test/reference test/svg

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Sat Dec 31 09:28:00 UTC 2022


 .gitlab-ci.yml                                 |    1 
 configure.ac                                   |    3 
 meson-cc-tests/fuzzer.c                        |    7 
 meson.build                                    |    8 
 src/Makefile.sources                           |    2 
 src/cairo-analysis-surface.c                   |   36 
 src/cairo-ft-font.c                            |  870 +++++-
 src/cairo-ft-private.h                         |   15 
 src/cairo-matrix.c                             |    1 
 src/cairo-pattern.c                            |    5 
 src/cairo-pdf-surface.c                        |    2 
 src/cairo-png.c                                |    1 
 src/cairo-recording-surface.c                  |    1 
 src/cairo-svg-glyph-render.c                   | 3225 +++++++++++++++++++++++++
 src/cairo-user-font.c                          |    2 
 src/cairo.c                                    |   17 
 src/cairoint.h                                 |  101 
 src/meson.build                                |    1 
 test/Makefile.am                               |    3 
 test/Makefile.sources                          |    3 
 test/cairo-logo-font.ttx                       |  539 ++++
 test/cairo-svg-test-doc.ttx                    |  689 +++++
 test/cairo-svg-test-fill.ttx                   |  365 ++
 test/cairo-svg-test-gradient.ttx               |  441 +++
 test/cairo-svg-test-path.ttx                   |  281 ++
 test/cairo-svg-test-shapes.ttx                 |  333 ++
 test/cairo-svg-test-stroke.ttx                 |  608 ++++
 test/cairo-svg-test-transform.ttx              |  403 +++
 test/cairo-test.c                              |   67 
 test/cairo-test.h                              |    5 
 test/ft-svg-cairo-logo.c                       |   66 
 test/ft-svg-color-font.c                       |  146 +
 test/ft-svg-render.c                           |  159 +
 test/meson.build                               |   46 
 test/reference/ft-svg-cairo-logo.ref.png       |binary
 test/reference/ft-svg-color-font.ref.png       |binary
 test/reference/ft-svg-render-doc.ref.png       |binary
 test/reference/ft-svg-render-fill.ref.png      |binary
 test/reference/ft-svg-render-gradient.ref.png  |binary
 test/reference/ft-svg-render-path.ref.png      |binary
 test/reference/ft-svg-render-shapes.ref.png    |binary
 test/reference/ft-svg-render-stroke.ref.png    |binary
 test/reference/ft-svg-render-transform.ref.png |binary
 test/svg/README                                |   22 
 test/svg/build_ttx_fonts.py                    |  161 +
 test/svg/doc.0.viewBox1.svg                    |    4 
 test/svg/doc.1.viewBox2.svg                    |    4 
 test/svg/doc.2.image.svg                       |  121 
 test/svg/doc.3.image-transform.svg             |  122 
 test/svg/doc.4.clip-user.svg                   |    9 
 test/svg/doc.5.clip-object.svg                 |   10 
 test/svg/doc.6.clip-user2.svg                  |   10 
 test/svg/doc.7.clip-object2.svg                |   11 
 test/svg/doc.8.clip-user3.svg                  |   15 
 test/svg/doc.9.clip-object3.svg                |   15 
 test/svg/doc.A.g.svg                           |   14 
 test/svg/fill.0.name.svg                       |    4 
 test/svg/fill.1.hex6.svg                       |    4 
 test/svg/fill.2.hex3.svg                       |    4 
 test/svg/fill.3.rgb.svg                        |    4 
 test/svg/fill.4.current-color.svg              |    4 
 test/svg/fill.5.palette.svg                    |    4 
 test/svg/fill.6.opacity.svg                    |    4 
 test/svg/fill.7.color.svg                      |    5 
 test/svg/fill.8.rule.svg                       |    8 
 test/svg/fuzzer/README                         |   19 
 test/svg/fuzzer/meson.build                    |   14 
 test/svg/fuzzer/svg-render-fuzzer.c            |   57 
 test/svg/gradient.0.lin-pad.svg                |   10 
 test/svg/gradient.1.lin-reflect.svg            |   11 
 test/svg/gradient.2.lin-repeat.svg             |   11 
 test/svg/gradient.3.lin-user.svg               |   11 
 test/svg/gradient.4.lin-transform.svg          |   10 
 test/svg/gradient.5.rad-pad.svg                |   10 
 test/svg/gradient.6.rad-reflect.svg            |   11 
 test/svg/gradient.7.rad-repeat.svg             |   11 
 test/svg/gradient.8.rad-user.svg               |   11 
 test/svg/gradient.9.rad-transform.svg          |   11 
 test/svg/meson.build                           |    9 
 test/svg/path.0.line.svg                       |    8 
 test/svg/path.1.curve.svg                      |    9 
 test/svg/path.2.quad.svg                       |    8 
 test/svg/path.3.arc.svg                        |    7 
 test/svg/shapes.0.rect.svg                     |    3 
 test/svg/shapes.1.rounded-rect.svg             |    3 
 test/svg/shapes.2.circle.svg                   |    3 
 test/svg/shapes.3.ellipse.svg                  |    3 
 test/svg/shapes.4.line.svg                     |    3 
 test/svg/shapes.5.polyline.svg                 |   12 
 test/svg/shapes.6.polygon.svg                  |   12 
 test/svg/stroke.0.name.svg                     |    6 
 test/svg/stroke.1.hex6.svg                     |    6 
 test/svg/stroke.2.hex3.svg                     |    6 
 test/svg/stroke.3.rgb.svg                      |    6 
 test/svg/stroke.4.current-color.svg            |    6 
 test/svg/stroke.5.palette.svg                  |    6 
 test/svg/stroke.6.opacity.svg                  |    6 
 test/svg/stroke.7.color.svg                    |    7 
 test/svg/stroke.8.width.svg                    |   14 
 test/svg/stroke.9.cap.svg                      |   17 
 test/svg/stroke.A.dash.svg                     |   27 
 test/svg/stroke.B.dash-offset.svg              |   31 
 test/svg/stroke.C.miter.svg                    |   10 
 test/svg/stroke.D.round.svg                    |   10 
 test/svg/stroke.E.bevel.svg                    |   10 
 test/svg/stroke.F.miter-limit.svg              |   11 
 test/svg/svg-font-template.ttx                 |  190 +
 test/svg/svg-render.c                          |  308 ++
 test/svg/transform.0.translate.svg             |    8 
 test/svg/transform.1.scale.svg                 |   10 
 test/svg/transform.2.rotate.svg                |   10 
 test/svg/transform.3.skewX.svg                 |   10 
 test/svg/transform.4.skewY.svg                 |   10 
 test/svg/transform.5.matrix.svg                |   10 
 test/svg/transform.6.multiple.svg              |   16 
 test/svg/transform.7.stroke.svg                |   20 
 116 files changed, 9837 insertions(+), 222 deletions(-)

New commits:
commit 3a60f6e138942af739b5998c521527e691ffeba4
Merge: 8c983c0d5 3e8b9a7cf
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Dec 31 09:27:57 2022 +0000

    Merge branch 'ft-svg-fonts' into 'master'
    
    Support SVG fonts in FT backend
    
    See merge request cairo/cairo!319

commit 3e8b9a7cf4417d862917ec3c50fe8f0f58142e46
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Dec 28 14:00:33 2022 +1030

    Add some missing slim_hidden entries

diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
index f3cf684c9..cb49adcbe 100644
--- a/src/cairo-matrix.c
+++ b/src/cairo-matrix.c
@@ -304,6 +304,7 @@ cairo_matrix_rotate (cairo_matrix_t *matrix, double radians)
 
     cairo_matrix_multiply (matrix, &tmp, matrix);
 }
+slim_hidden_def (cairo_matrix_rotate);
 
 /**
  * cairo_matrix_multiply:
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index e969f0a17..2cbd52b2c 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -812,6 +812,7 @@ cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
 
     return &pattern->base.base;
 }
+slim_hidden_def (cairo_pattern_create_linear);
 
 /**
  * cairo_pattern_create_radial:
@@ -866,6 +867,7 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0,
 
     return &pattern->base.base;
 }
+slim_hidden_def (cairo_pattern_create_radial);
 
 /* This order is specified in the diagram in the documentation for
  * cairo_pattern_create_mesh() */
@@ -1093,6 +1095,7 @@ cairo_pattern_get_type (cairo_pattern_t *pattern)
 {
     return pattern->type;
 }
+slim_hidden_def (cairo_pattern_get_type);
 
 /**
  * cairo_pattern_status:
@@ -2117,6 +2120,7 @@ cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
     pattern->extend = extend;
     _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_EXTEND);
 }
+slim_hidden_def (cairo_pattern_set_extend);
 
 /**
  * cairo_pattern_get_extend:
@@ -4197,6 +4201,7 @@ cairo_pattern_get_rgba (cairo_pattern_t *pattern,
 
     return CAIRO_STATUS_SUCCESS;
 }
+slim_hidden_def (cairo_pattern_get_rgba);
 
 /**
  * cairo_pattern_get_surface:
diff --git a/src/cairo-png.c b/src/cairo-png.c
index 4b7c34081..5b9c58447 100644
--- a/src/cairo-png.c
+++ b/src/cairo-png.c
@@ -987,3 +987,4 @@ cairo_image_surface_create_from_png_stream (cairo_read_func_t	read_func,
 
     return read_png (&png_closure);
 }
+slim_hidden_def (cairo_image_surface_create_from_png_stream);
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index 065e62c46..d6b6ab337 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -2309,6 +2309,7 @@ DONE:
     if (height)
 	*height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y);
 }
+slim_hidden_def (cairo_recording_surface_ink_extents);
 
 cairo_status_t
 _cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface,
diff --git a/src/cairo.c b/src/cairo.c
index e9c4bb9a8..7bfc6a143 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -706,6 +706,7 @@ cairo_push_group (cairo_t *cr)
 {
     cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
 }
+slim_hidden_def (cairo_push_group);
 
 /**
  * cairo_push_group_with_content:
@@ -813,6 +814,7 @@ cairo_pop_group_to_source (cairo_t *cr)
     cairo_set_source (cr, group_pattern);
     cairo_pattern_destroy (group_pattern);
 }
+slim_hidden_def (cairo_pop_group_to_source);
 
 /**
  * cairo_set_operator:
@@ -939,6 +941,7 @@ cairo_set_source_rgba (cairo_t *cr,
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_set_source_rgba);
 
 /**
  * cairo_set_source_surface:
@@ -1052,6 +1055,7 @@ cairo_get_source (cairo_t *cr)
 
     return cr->backend->get_source (cr);
 }
+slim_hidden_def (cairo_get_source);
 
 /**
  * cairo_set_tolerance:
@@ -1340,6 +1344,7 @@ cairo_set_dash (cairo_t	     *cr,
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_set_dash);
 
 /**
  * cairo_get_dash_count:
@@ -1514,6 +1519,7 @@ cairo_rotate (cairo_t *cr, double angle)
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_rotate);
 
 /**
  * cairo_transform:
@@ -1903,6 +1909,7 @@ cairo_arc (cairo_t *cr,
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_arc);
 
 /**
  * cairo_arc_negative:
@@ -1948,6 +1955,7 @@ cairo_arc_negative (cairo_t *cr,
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_arc_negative);
 
 /* XXX: NYI
 void
@@ -2130,6 +2138,7 @@ cairo_rectangle (cairo_t *cr,
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_rectangle);
 
 #if 0
 /* XXX: NYI */
@@ -2290,6 +2299,7 @@ cairo_paint_with_alpha (cairo_t *cr,
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_paint_with_alpha);
 
 /**
  * cairo_mask:
@@ -2465,6 +2475,7 @@ cairo_fill (cairo_t *cr)
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_fill);
 
 /**
  * cairo_fill_preserve:
@@ -2756,6 +2767,7 @@ cairo_clip (cairo_t *cr)
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_clip);
 
 /**
  * cairo_clip_preserve:
@@ -4006,6 +4018,7 @@ cairo_has_current_point (cairo_t *cr)
 
     return cr->backend->has_current_point (cr);
 }
+slim_hidden_def (cairo_has_current_point);
 
 /**
  * cairo_get_current_point:
@@ -4076,6 +4089,7 @@ cairo_get_fill_rule (cairo_t *cr)
 
     return cr->backend->get_fill_rule (cr);
 }
+slim_hidden_def (cairo_set_fill_rule);
 
 /**
  * cairo_get_line_width:
@@ -4176,6 +4190,7 @@ cairo_get_miter_limit (cairo_t *cr)
 
     return cr->backend->get_miter_limit (cr);
 }
+slim_hidden_def (cairo_set_miter_limit);
 
 /**
  * cairo_get_matrix:
@@ -4291,6 +4306,7 @@ cairo_copy_path (cairo_t *cr)
 
     return cr->backend->copy_path (cr);
 }
+slim_hidden_def (cairo_copy_path);
 
 /**
  * cairo_copy_path_flat:
@@ -4385,6 +4401,7 @@ cairo_append_path (cairo_t		*cr,
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_append_path);
 
 /**
  * cairo_status:
diff --git a/src/cairoint.h b/src/cairoint.h
index 987bf9a58..af0f0fadc 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1939,11 +1939,18 @@ cairo_private cairo_status_t
 _cairo_fopen (const char *filename, const char *mode, FILE **file_out);
 
 /* Avoid unnecessary PLT entries.  */
+slim_hidden_proto (cairo_append_path);
+slim_hidden_proto (cairo_arc);
+slim_hidden_proto (cairo_arc_negative);
+slim_hidden_proto (cairo_clip);
 slim_hidden_proto (cairo_clip_preserve);
 slim_hidden_proto (cairo_close_path);
+slim_hidden_proto (cairo_copy_path);
 slim_hidden_proto (cairo_create);
 slim_hidden_proto (cairo_curve_to);
 slim_hidden_proto (cairo_destroy);
+slim_hidden_proto (cairo_device_to_user);
+slim_hidden_proto (cairo_fill);
 slim_hidden_proto (cairo_fill_preserve);
 slim_hidden_proto (cairo_font_face_destroy);
 slim_hidden_proto (cairo_font_face_get_user_data);
@@ -1959,14 +1966,16 @@ slim_hidden_proto (cairo_font_options_set_subpixel_order);
 slim_hidden_proto (cairo_font_options_status);
 slim_hidden_proto (cairo_format_stride_for_width);
 slim_hidden_proto (cairo_get_current_point);
-slim_hidden_proto (cairo_get_line_width);
 slim_hidden_proto (cairo_get_hairline);
+slim_hidden_proto (cairo_get_line_width);
 slim_hidden_proto (cairo_get_matrix);
 slim_hidden_proto (cairo_get_scaled_font);
+slim_hidden_proto (cairo_get_source);
 slim_hidden_proto (cairo_get_target);
 slim_hidden_proto (cairo_get_tolerance);
 slim_hidden_proto (cairo_glyph_allocate);
 slim_hidden_proto (cairo_glyph_free);
+slim_hidden_proto (cairo_has_current_point);
 slim_hidden_proto (cairo_image_surface_create);
 slim_hidden_proto (cairo_image_surface_create_for_data);
 slim_hidden_proto (cairo_image_surface_get_data);
@@ -1983,35 +1992,70 @@ slim_hidden_proto (cairo_matrix_init_scale);
 slim_hidden_proto (cairo_matrix_init_translate);
 slim_hidden_proto (cairo_matrix_invert);
 slim_hidden_proto (cairo_matrix_multiply);
+slim_hidden_proto (cairo_matrix_rotate);
 slim_hidden_proto (cairo_matrix_scale);
 slim_hidden_proto (cairo_matrix_transform_distance);
 slim_hidden_proto (cairo_matrix_transform_point);
 slim_hidden_proto (cairo_matrix_translate);
+slim_hidden_proto (cairo_mesh_pattern_curve_to);
+slim_hidden_proto (cairo_mesh_pattern_get_control_point);
+slim_hidden_proto (cairo_mesh_pattern_get_corner_color_rgba);
+slim_hidden_proto (cairo_mesh_pattern_get_patch_count);
+slim_hidden_proto (cairo_mesh_pattern_get_path);
+slim_hidden_proto (cairo_mesh_pattern_line_to);
+slim_hidden_proto (cairo_mesh_pattern_move_to);
+slim_hidden_proto (cairo_mesh_pattern_set_corner_color_rgba);
 slim_hidden_proto (cairo_move_to);
 slim_hidden_proto (cairo_new_path);
 slim_hidden_proto (cairo_paint);
+slim_hidden_proto (cairo_paint_with_alpha);
+slim_hidden_proto_no_warn (cairo_path_destroy);
 slim_hidden_proto (cairo_pattern_add_color_stop_rgba);
 slim_hidden_proto (cairo_pattern_create_for_surface);
+slim_hidden_proto (cairo_pattern_create_linear);
+slim_hidden_proto (cairo_pattern_create_radial);
 slim_hidden_proto (cairo_pattern_create_rgb);
 slim_hidden_proto (cairo_pattern_create_rgba);
 slim_hidden_proto (cairo_pattern_destroy);
 slim_hidden_proto (cairo_pattern_get_extend);
-slim_hidden_proto (cairo_mesh_pattern_curve_to);
-slim_hidden_proto (cairo_mesh_pattern_get_control_point);
-slim_hidden_proto (cairo_mesh_pattern_get_corner_color_rgba);
-slim_hidden_proto (cairo_mesh_pattern_get_patch_count);
-slim_hidden_proto (cairo_mesh_pattern_get_path);
-slim_hidden_proto (cairo_mesh_pattern_line_to);
-slim_hidden_proto (cairo_mesh_pattern_move_to);
-slim_hidden_proto (cairo_mesh_pattern_set_corner_color_rgba);
+slim_hidden_proto (cairo_pattern_get_rgba);
+slim_hidden_proto (cairo_pattern_get_type);
 slim_hidden_proto_no_warn (cairo_pattern_reference);
+slim_hidden_proto (cairo_pattern_set_extend);
 slim_hidden_proto (cairo_pattern_set_matrix);
 slim_hidden_proto (cairo_pop_group);
+slim_hidden_proto (cairo_pop_group_to_source);
+slim_hidden_proto (cairo_push_group);
 slim_hidden_proto (cairo_push_group_with_content);
-slim_hidden_proto_no_warn (cairo_path_destroy);
 slim_hidden_proto (cairo_recording_surface_create);
+slim_hidden_proto (cairo_recording_surface_ink_extents);
+slim_hidden_proto (cairo_rectangle);
+slim_hidden_proto (cairo_region_contains_point);
+slim_hidden_proto (cairo_region_contains_rectangle);
+slim_hidden_proto (cairo_region_copy);
+slim_hidden_proto (cairo_region_create);
+slim_hidden_proto (cairo_region_create_rectangle);
+slim_hidden_proto (cairo_region_create_rectangles);
+slim_hidden_proto (cairo_region_destroy);
+slim_hidden_proto (cairo_region_equal);
+slim_hidden_proto (cairo_region_get_extents);
+slim_hidden_proto (cairo_region_get_rectangle);
+slim_hidden_proto (cairo_region_intersect);
+slim_hidden_proto (cairo_region_intersect_rectangle);
+slim_hidden_proto (cairo_region_is_empty);
+slim_hidden_proto (cairo_region_num_rectangles);
+slim_hidden_proto (cairo_region_reference);
+slim_hidden_proto (cairo_region_status);
+slim_hidden_proto (cairo_region_subtract);
+slim_hidden_proto (cairo_region_subtract_rectangle);
+slim_hidden_proto (cairo_region_translate);
+slim_hidden_proto (cairo_region_union);
+slim_hidden_proto (cairo_region_union_rectangle);
+slim_hidden_proto (cairo_region_xor);
+slim_hidden_proto (cairo_region_xor_rectangle);
 slim_hidden_proto (cairo_rel_line_to);
 slim_hidden_proto (cairo_restore);
+slim_hidden_proto (cairo_rotate);
 slim_hidden_proto (cairo_save);
 slim_hidden_proto (cairo_scale);
 slim_hidden_proto (cairo_scaled_font_create);
@@ -2021,23 +2065,27 @@ slim_hidden_proto (cairo_scaled_font_get_ctm);
 slim_hidden_proto (cairo_scaled_font_get_font_face);
 slim_hidden_proto (cairo_scaled_font_get_font_matrix);
 slim_hidden_proto (cairo_scaled_font_get_font_options);
+slim_hidden_proto (cairo_scaled_font_get_user_data);
 slim_hidden_proto (cairo_scaled_font_glyph_extents);
 slim_hidden_proto_no_warn (cairo_scaled_font_reference);
-slim_hidden_proto (cairo_scaled_font_status);
-slim_hidden_proto (cairo_scaled_font_get_user_data);
 slim_hidden_proto (cairo_scaled_font_set_user_data);
+slim_hidden_proto (cairo_scaled_font_status);
 slim_hidden_proto (cairo_scaled_font_text_to_glyphs);
+slim_hidden_proto (cairo_set_dash);
+slim_hidden_proto (cairo_set_fill_rule);
 slim_hidden_proto (cairo_set_font_matrix);
 slim_hidden_proto (cairo_set_font_options);
 slim_hidden_proto (cairo_set_font_size);
+slim_hidden_proto (cairo_set_hairline);
 slim_hidden_proto (cairo_set_line_cap);
 slim_hidden_proto (cairo_set_line_join);
 slim_hidden_proto (cairo_set_line_width);
-slim_hidden_proto (cairo_set_hairline);
 slim_hidden_proto (cairo_set_matrix);
+slim_hidden_proto (cairo_set_miter_limit);
 slim_hidden_proto (cairo_set_operator);
 slim_hidden_proto (cairo_set_source);
 slim_hidden_proto (cairo_set_source_rgb);
+slim_hidden_proto (cairo_set_source_rgba);
 slim_hidden_proto (cairo_set_source_surface);
 slim_hidden_proto (cairo_set_tolerance);
 slim_hidden_proto (cairo_status);
@@ -2068,43 +2116,20 @@ slim_hidden_proto (cairo_text_cluster_free);
 slim_hidden_proto (cairo_toy_font_face_create);
 slim_hidden_proto (cairo_toy_font_face_get_slant);
 slim_hidden_proto (cairo_toy_font_face_get_weight);
-slim_hidden_proto (cairo_translate);
 slim_hidden_proto (cairo_transform);
+slim_hidden_proto (cairo_translate);
 slim_hidden_proto (cairo_user_font_face_create);
 slim_hidden_proto (cairo_user_font_face_set_init_func);
 slim_hidden_proto (cairo_user_font_face_set_render_color_glyph_func);
 slim_hidden_proto (cairo_user_font_face_set_render_glyph_func);
 slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func);
-slim_hidden_proto (cairo_device_to_user);
 slim_hidden_proto (cairo_user_to_device);
 slim_hidden_proto (cairo_user_to_device_distance);
 slim_hidden_proto (cairo_version_string);
-slim_hidden_proto (cairo_region_create);
-slim_hidden_proto (cairo_region_create_rectangle);
-slim_hidden_proto (cairo_region_create_rectangles);
-slim_hidden_proto (cairo_region_copy);
-slim_hidden_proto (cairo_region_reference);
-slim_hidden_proto (cairo_region_destroy);
-slim_hidden_proto (cairo_region_equal);
-slim_hidden_proto (cairo_region_status);
-slim_hidden_proto (cairo_region_get_extents);
-slim_hidden_proto (cairo_region_num_rectangles);
-slim_hidden_proto (cairo_region_get_rectangle);
-slim_hidden_proto (cairo_region_is_empty);
-slim_hidden_proto (cairo_region_contains_rectangle);
-slim_hidden_proto (cairo_region_contains_point);
-slim_hidden_proto (cairo_region_translate);
-slim_hidden_proto (cairo_region_subtract);
-slim_hidden_proto (cairo_region_subtract_rectangle);
-slim_hidden_proto (cairo_region_intersect);
-slim_hidden_proto (cairo_region_intersect_rectangle);
-slim_hidden_proto (cairo_region_union);
-slim_hidden_proto (cairo_region_union_rectangle);
-slim_hidden_proto (cairo_region_xor);
-slim_hidden_proto (cairo_region_xor_rectangle);
 
 #if CAIRO_HAS_PNG_FUNCTIONS
 
+slim_hidden_proto (cairo_image_surface_create_from_png_stream);
 slim_hidden_proto (cairo_surface_write_to_png_stream);
 
 #endif
commit 1ba3e40d9c4a51d9697b6d9ff90a75bb419dd43e
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Dec 28 12:44:46 2022 +1030

    FT: support COLRv0 recording surface

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index c27800b00..100f839d7 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -44,6 +44,8 @@
 #include "cairo-error-private.h"
 #include "cairo-image-surface-private.h"
 #include "cairo-ft-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-path-private.h"
 #include "cairo-pattern-private.h"
 #include "cairo-pixman-private.h"
 #include "cairo-recording-surface-private.h"
@@ -2491,6 +2493,99 @@ _cairo_ft_scaled_glyph_load_glyph (cairo_ft_scaled_font_t *scaled_font,
     return CAIRO_STATUS_SUCCESS;
 }
 
+typedef enum {
+    CAIRO_FT_GLYPH_TYPE_BITMAP,
+    CAIRO_FT_GLYPH_TYPE_OUTLINE,
+    CAIRO_FT_GLYPH_TYPE_SVG,
+    CAIRO_FT_GLYPH_TYPE_COLR_V0,
+} cairo_ft_glyph_format_t;
+
+typedef struct {
+    cairo_scaled_glyph_private_t base;
+
+    cairo_ft_glyph_format_t format;
+} cairo_ft_glyph_private_t;
+
+static void
+_cairo_ft_glyph_fini (cairo_scaled_glyph_private_t *glyph_private,
+		      cairo_scaled_glyph_t *glyph,
+		      cairo_scaled_font_t  *font)
+{
+    cairo_list_del (&glyph_private->link);
+    free (glyph_private);
+}
+
+
+#ifdef HAVE_FT_PALETTE_SELECT
+static void
+_cairo_ft_scaled_glyph_set_palette (cairo_ft_scaled_font_t  *scaled_font,
+				    FT_Face                  face,
+				    unsigned int            *num_entries_ret,
+				    FT_Color               **entries_ret)
+{
+    FT_Palette_Data palette_data;
+    unsigned int num_entries;
+    FT_Color *entries;
+
+    num_entries = 0;
+    entries = NULL;
+    if (FT_Palette_Data_Get (face, &palette_data) == 0 && palette_data.num_palettes > 0) {
+	FT_UShort palette_index = CAIRO_COLOR_PALETTE_DEFAULT;
+	if (scaled_font->base.options.palette_index < palette_data.num_palettes)
+	    palette_index = scaled_font->base.options.palette_index;
+
+	num_entries = palette_data.num_palettes;
+	if (FT_Palette_Select (face, palette_index, &entries) != 0) {
+	    num_entries = 0;
+	    entries = NULL;
+	}
+    }
+    if (num_entries_ret)
+	*num_entries_ret = num_entries;
+
+    if (entries_ret)
+	*entries_ret = entries;
+}
+#endif
+
+/* returns TRUE if foreground color used */
+static cairo_bool_t
+_cairo_ft_scaled_glyph_set_foreground_color (cairo_ft_scaled_font_t *scaled_font,
+					     cairo_scaled_glyph_t   *scaled_glyph,
+					     FT_Face                 face,
+					     const cairo_color_t    *foreground_color)
+{
+    cairo_bool_t uses_foreground_color = FALSE;
+#ifdef HAVE_FT_PALETTE_SELECT
+    FT_LayerIterator  iterator;
+    FT_UInt layer_glyph_index;
+    FT_UInt layer_color_index;
+    FT_Color color;
+
+    /* Check if there is a layer that uses the foreground color */
+    iterator.p  = NULL;
+    while (FT_Get_Color_Glyph_Layer(face,
+				    _cairo_scaled_glyph_index (scaled_glyph),
+				    &layer_glyph_index,
+				    &layer_color_index,
+				    &iterator)) {
+	if (layer_color_index == 0xFFFF) {
+	    uses_foreground_color = TRUE;
+	    break;
+	}
+    }
+
+    if (uses_foreground_color) {
+	color.red = (FT_Byte)(foreground_color->red * 0xFF);
+	color.green = (FT_Byte)(foreground_color->green * 0xFF);
+	color.blue = (FT_Byte)(foreground_color->blue * 0xFF);
+	color.alpha = (FT_Byte)(foreground_color->alpha * 0xFF);
+	FT_Palette_Set_Foreground_Color (face, color);
+    }
+#endif
+    return uses_foreground_color;
+}
+
 static cairo_int_status_t
 _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t     *scaled_font,
 				     cairo_scaled_glyph_t	*scaled_glyph,
@@ -2516,41 +2611,12 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t     *scaled_font,
 	    return CAIRO_INT_STATUS_UNSUPPORTED;
 	}
 
+	uses_foreground_color = _cairo_ft_scaled_glyph_set_foreground_color (scaled_font,
+									     scaled_glyph,
+									     face,
+									     foreground_color);
 #ifdef HAVE_FT_PALETTE_SELECT
-	FT_LayerIterator  iterator;
-	FT_UInt layer_glyph_index;
-	FT_UInt layer_color_index;
-	FT_Color color;
-	FT_Palette_Data palette_data;
-
-	/* Check if there is a layer that uses the foreground color */
-	iterator.p  = NULL;
-	while (FT_Get_Color_Glyph_Layer(face,
-					_cairo_scaled_glyph_index(scaled_glyph),
-					&layer_glyph_index,
-					&layer_color_index,
-					&iterator)) {
-	    if (layer_color_index == 0xFFFF) {
-		uses_foreground_color = TRUE;
-		break;
-	    }
-	}
-
-	if (uses_foreground_color) {
-	    color.red = (FT_Byte)(foreground_color->red * 0xFF);
-	    color.green = (FT_Byte)(foreground_color->green * 0xFF);
-	    color.blue = (FT_Byte)(foreground_color->blue * 0xFF);
-	    color.alpha = (FT_Byte)(foreground_color->alpha * 0xFF);
-	    FT_Palette_Set_Foreground_Color (face, color);
-	}
-
-	if (FT_Palette_Data_Get (face, &palette_data) == 0 && palette_data.num_palettes > 0) {
-	    FT_UShort palette_index = CAIRO_COLOR_PALETTE_DEFAULT;
-	    if (scaled_font->base.options.palette_index < palette_data.num_palettes)
-		palette_index = scaled_font->base.options.palette_index;
-
-	    FT_Palette_Select (face, palette_index, NULL);
-	}
+	_cairo_ft_scaled_glyph_set_palette (scaled_font, face, NULL, NULL);
 #endif
 
         load_flags &= ~FT_LOAD_MONOCHROME;
@@ -2626,6 +2692,100 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t     *scaled_font,
     return status;
 }
 
+#ifdef HAVE_FT_PALETTE_SELECT
+static cairo_int_status_t
+_cairo_ft_scaled_glyph_init_record_colr_v0_glyph (cairo_ft_scaled_font_t *scaled_font,
+						  cairo_scaled_glyph_t   *scaled_glyph,
+						  FT_Face                 face,
+						  cairo_bool_t            vertical_layout,
+						  int                     load_flags)
+{
+    cairo_surface_t *recording_surface;
+    cairo_t *cr;
+    cairo_status_t status;
+    FT_Color *palette;
+    unsigned int num_palette_entries;
+    FT_LayerIterator iterator;
+    FT_UInt layer_glyph_index;
+    FT_UInt layer_color_index;
+    cairo_path_fixed_t *path_fixed;
+    cairo_path_t *path;
+
+    _cairo_ft_scaled_glyph_set_palette (scaled_font, face, &num_palette_entries, &palette);
+
+    load_flags &= ~FT_LOAD_MONOCHROME;
+    /* clear load target mode */
+    load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(load_flags)));
+    load_flags |= FT_LOAD_TARGET_NORMAL;
+    load_flags |= FT_LOAD_COLOR;
+
+    recording_surface =
+	cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+
+    cr = cairo_create (recording_surface);
+
+    if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) {
+        cairo_matrix_t scale;
+	scale = scaled_font->base.scale;
+	scale.x0 = scale.y0 = 0.;
+	cairo_set_matrix (cr, &scale);
+    }
+
+    iterator.p  = NULL;
+    while (FT_Get_Color_Glyph_Layer(face,
+				    _cairo_scaled_glyph_index (scaled_glyph),
+				    &layer_glyph_index,
+				    &layer_color_index,
+				    &iterator))
+    {
+	cairo_pattern_t *pattern;
+	if (layer_color_index == 0xFFFF) {
+	    pattern = cairo_pattern_create_rgb (0, 0, 0);
+	    pattern->is_userfont_foreground = TRUE;
+	} else {
+	    double r = 0, g = 0, b = 0, a = 1;
+	    if (layer_color_index <  num_palette_entries) {
+		FT_Color *color = &palette[layer_color_index];
+		r = color->red / 255.0;
+		g = color->green/ 255.0;
+		b = color->blue / 255.0;
+		a = color->alpha / 255.0;
+	    }
+	    pattern = cairo_pattern_create_rgba (r, g, b, a);
+	}
+	cairo_set_source (cr, pattern);
+	cairo_pattern_destroy (pattern);
+
+	if (FT_Load_Glyph (face, layer_glyph_index, load_flags) != 0) {
+	    status = CAIRO_INT_STATUS_UNSUPPORTED;
+	    goto cleanup;
+	}
+
+	status = _decompose_glyph_outline (face, &scaled_font->ft_options.base, &path_fixed);
+	if (unlikely (status))
+	    return status;
+
+	path = _cairo_path_create (path_fixed, cr);
+	_cairo_path_fixed_destroy (path_fixed);
+	cairo_append_path(cr, path);
+	cairo_fill (cr);
+    }
+
+  cleanup:
+    cairo_destroy (cr);
+
+    if (status) {
+	cairo_surface_destroy (recording_surface);
+	return status;
+    }
+
+    _cairo_scaled_glyph_set_recording_surface (scaled_glyph,
+					       &scaled_font->base,
+					       recording_surface);
+    return status;
+}
+#endif
+
 #if HAVE_FT_SVG_DOCUMENT
 static cairo_int_status_t
 _cairo_ft_scaled_glyph_init_record_svg_glyph (cairo_ft_scaled_font_t *scaled_font,
@@ -2639,8 +2799,8 @@ _cairo_ft_scaled_glyph_init_record_svg_glyph (cairo_ft_scaled_font_t *scaled_fon
     cairo_pattern_t *pattern;
     FT_SVG_Document svg_doc = face->glyph->other;
     char *svg_document;
-    FT_Color*        palette;
-    FT_Palette_Data  palette_data;
+    FT_Color *palette;
+    unsigned int num_palette_entries;
 
     /* Create NULL terminated SVG document */
     svg_document = strndup((const char*)svg_doc->svg_document, svg_doc->svg_document_length);
@@ -2670,15 +2830,7 @@ _cairo_ft_scaled_glyph_init_record_svg_glyph (cairo_ft_scaled_font_t *scaled_fon
     extents->width = DOUBLE_FROM_26_6(face->bbox.xMax) - extents->x_bearing;
     extents->height = DOUBLE_FROM_26_6(face->bbox.yMax) - extents->y_bearing;
 
-    palette = NULL;
-    if (FT_Palette_Data_Get (face, &palette_data) == 0 && palette_data.num_palettes > 0) {
-	FT_UShort palette_index = CAIRO_COLOR_PALETTE_DEFAULT;
-	if (scaled_font->base.options.palette_index < palette_data.num_palettes)
-	    palette_index = scaled_font->base.options.palette_index;
-
-	if (FT_Palette_Select (face, palette_index, &palette) != 0)
-	    palette = NULL;
-    }
+    _cairo_ft_scaled_glyph_set_palette (scaled_font, face, &num_palette_entries, &palette);
 
     if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) {
 	status = _cairo_render_svg_glyph (svg_document,
@@ -2687,7 +2839,7 @@ _cairo_ft_scaled_glyph_init_record_svg_glyph (cairo_ft_scaled_font_t *scaled_fon
 					  _cairo_scaled_glyph_index(scaled_glyph),
 					  svg_doc->units_per_EM,
 					  palette,
-					  palette ? palette_data.num_palette_entries : 0,
+					  num_palette_entries,
 					  cr);
 	if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
 	    status = CAIRO_INT_STATUS_UNSUPPORTED;
@@ -2927,53 +3079,33 @@ _cairo_ft_scaled_glyph_get_metrics (cairo_ft_scaled_font_t     *scaled_font,
     }
 }
 
-static cairo_int_status_t
-_cairo_ft_scaled_glyph_init_metrics_svg_glyph (cairo_ft_scaled_font_t     *scaled_font,
-					       cairo_scaled_glyph_t	*scaled_glyph,
-					       FT_Face face,
-					       cairo_bool_t vertical_layout,
-					       int load_flags)
-{
-    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
-#if HAVE_FT_SVG_DOCUMENT
-    cairo_text_extents_t fs_metrics;
-    cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
-
-    if (scaled_font->base.options.color_mode == CAIRO_COLOR_MODE_NO_COLOR)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-
-    status = _cairo_ft_scaled_glyph_load_glyph (scaled_font,
-						scaled_glyph,
-						face,
-						load_flags | FT_LOAD_COLOR,
-						!hint_metrics,
-						vertical_layout);
-    if (unlikely (status))
-	return status;
-
-    if (face->glyph->format != FT_GLYPH_FORMAT_SVG)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-
-    /* Get the advance. The other metrics are ignored */
-    _cairo_ft_scaled_glyph_get_metrics (scaled_font,
-					face,
-					vertical_layout,
-					load_flags,
-					&fs_metrics);
+static cairo_bool_t
+_cairo_ft_scaled_glyph_is_colr_v0 (cairo_ft_scaled_font_t *scaled_font,
+				   cairo_scaled_glyph_t	  *scaled_glyph,
+				   FT_Face                 face)
 
-    status = (cairo_int_status_t)_cairo_ft_scaled_glyph_init_record_svg_glyph (scaled_font,
-									       scaled_glyph,
-									       face,
-									       &fs_metrics);
-    if (status == CAIRO_INT_STATUS_SUCCESS) {
-	_cairo_scaled_glyph_set_metrics (scaled_glyph,
-					 &scaled_font->base,
-					 &fs_metrics);
+{
+#ifdef HAVE_FT_PALETTE_SELECT
+    FT_LayerIterator  iterator;
+    FT_UInt layer_glyph_index;
+    FT_UInt layer_color_index;
+
+    iterator.p  = NULL;
+    if (FT_Get_Color_Glyph_Layer(face,
+				 _cairo_scaled_glyph_index (scaled_glyph),
+				 &layer_glyph_index,
+				 &layer_color_index,
+				 &iterator))
+    {
+	return TRUE;
     }
 #endif
-    return status;
+
+    return FALSE;
 }
 
+static const int ft_glyph_private_key;
+
 static cairo_int_status_t
 _cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
 				     cairo_scaled_glyph_t	*scaled_glyph,
@@ -2983,32 +3115,25 @@ _cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
 {
     cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
     cairo_text_extents_t fs_metrics;
+    cairo_ft_glyph_private_t *glyph_priv;
 
     cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
 
     /* _cairo_ft_scaled_glyph_init_metrics() is called once the first
-     * time a cairo_scaled_glyph_t is created. We first check if this
-     * is an SVG glyph as SVG glyphs require the bounding box to be
-     * obtained from the ink extents of the SVG rendering.
-     *
-     * If an SVG glyph is found and succesfully rendered to a
-     * recording surface, the presence of
-     * CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE in the
-     * cairo_scaled_glyph_t indicates that this is an SVG glyph.
+     * time a cairo_scaled_glyph_t is created. We first allocate the
+     * cairo_ft_glyph_private_t struct and determine the glyph type.
      */
-    status = _cairo_ft_scaled_glyph_init_metrics_svg_glyph (scaled_font,
-							    scaled_glyph,
-							    face,
-							    vertical_layout,
-							    load_flags);
-    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
-	return status;
 
-    /* The font metrics for color glyphs should be the same as the
-     * outline glyphs. But just in case there aren't, request the
-     * color or outline metrics based on the font option and if the
-     * font has color.
-     */
+    glyph_priv = _cairo_malloc (sizeof (*glyph_priv));
+    if (unlikely (glyph_priv == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_scaled_glyph_attach_private (scaled_glyph, &glyph_priv->base,
+					&ft_glyph_private_key,
+					_cairo_ft_glyph_fini);
+    scaled_glyph->dev_private = glyph_priv;
+
+    /* We need to load color to determine if this is a color format. */
     int color_flag = 0;
 #ifdef FT_LOAD_COLOR
     if (scaled_font->unscaled->have_color && scaled_font->base.options.color_mode != CAIRO_COLOR_MODE_NO_COLOR)
@@ -3023,12 +3148,45 @@ _cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
     if (unlikely (status))
 	return status;
 
+    cairo_bool_t is_svg_format = FALSE;
+#if HAVE_FT_SVG_DOCUMENT
+    if (face->glyph->format == FT_GLYPH_FORMAT_SVG)
+	is_svg_format = TRUE;
+#endif
+
+    if (is_svg_format) {
+         glyph_priv->format = CAIRO_FT_GLYPH_TYPE_SVG;
+    } else if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
+	if (_cairo_ft_scaled_glyph_is_colr_v0 (scaled_font, scaled_glyph, face))
+	    glyph_priv->format = CAIRO_FT_GLYPH_TYPE_COLR_V0;
+	else
+	    glyph_priv->format = CAIRO_FT_GLYPH_TYPE_OUTLINE;
+    } else {
+	/* For anything else we let FreeType render a bitmap. */
+	 glyph_priv->format =  CAIRO_FT_GLYPH_TYPE_BITMAP;
+    }
+
     _cairo_ft_scaled_glyph_get_metrics (scaled_font,
 					face,
 					vertical_layout,
 					load_flags,
 					&fs_metrics);
 
+#if HAVE_FT_SVG_DOCUMENT
+    if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_SVG) {
+	/* SVG glyphs require the bounding box to be obtained from the
+	 * ink extents of the SVG rendering. We need to render the SVG
+	 * to a recording surface to obtain these extents. But we also
+	 * need the advance from _cairo_ft_scaled_glyph_get_metrics()
+	 * before calling this function.
+	 */
+	status = (cairo_int_status_t)_cairo_ft_scaled_glyph_init_record_svg_glyph (scaled_font,
+										   scaled_glyph,
+										   face,
+										   &fs_metrics);
+    }
+#endif
+
     _cairo_scaled_glyph_set_metrics (scaled_glyph,
 				     &scaled_font->base,
 				     &fs_metrics);
@@ -3049,6 +3207,7 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
     cairo_bool_t vertical_layout = FALSE;
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
     cairo_bool_t scaled_glyph_loaded = FALSE;
+    cairo_ft_glyph_private_t *glyph_priv;
 
     face = _cairo_ft_unscaled_font_lock_face (unscaled);
     if (!face)
@@ -3082,13 +3241,39 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
 	    goto FAIL;
     }
 
+    /* scaled_glyph->dev_private is intialized by _cairo_ft_scaled_glyph_init_metrics() */
+    glyph_priv = scaled_glyph->dev_private;
+    assert (glyph_priv != NULL);
+
+    if (info & CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE) {
+	switch (glyph_priv->format) {
+	    case CAIRO_FT_GLYPH_TYPE_BITMAP:
+	    case CAIRO_FT_GLYPH_TYPE_OUTLINE:
+		status = CAIRO_INT_STATUS_UNSUPPORTED;
+		break;
+	    case CAIRO_FT_GLYPH_TYPE_SVG:
+		/* The SVG recording surface is initialized in _cairo_ft_scaled_glyph_init_metrics() */
+		status = CAIRO_STATUS_SUCCESS;
+		break;
+	    case CAIRO_FT_GLYPH_TYPE_COLR_V0:
+#ifdef HAVE_FT_PALETTE_SELECT
+		status = _cairo_ft_scaled_glyph_init_record_colr_v0_glyph (scaled_font,
+									   scaled_glyph,
+									   face,
+									   vertical_layout,
+									   load_flags);
+#endif
+		break;
+	}
+	if (status)
+	    goto FAIL;
+    }
+
     if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) {
-	if (scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE) {
+	if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_SVG) {
 	    status = _cairo_ft_scaled_glyph_init_surface_svg_glyph (scaled_font,
 								    scaled_glyph,
 								    foreground_color);
-	    if (unlikely (status))
-		goto FAIL;
 	} else {
 	    status = _cairo_ft_scaled_glyph_init_surface (scaled_font,
 							  scaled_glyph,
@@ -3097,9 +3282,9 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
 							  foreground_color,
 							  vertical_layout,
 							  load_flags);
-	    if (unlikely (status))
-		goto FAIL;
 	}
+	if (unlikely (status))
+	    goto FAIL;
     }
 
     if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) {
commit 4f9b6371238a8b9c88ad4644159ec5d581386968
Author: Jonathan Kew <jfkthame at googlemail.com>
Date:   Mon Jun 27 12:36:25 2022 +0000

    Fix x/y typo in _cairo_pdf_surface_analyze_operation
    
    This can result in spuriously returning UNSUPPORTED and generating rasterized output in cases where this isn't actually necessary.

diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 9b2b93252..bbd81c7b2 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -7630,7 +7630,7 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t  *surface,
 		    if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) {
 			if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x ||
 			    _cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y ||
-			    _cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width ||
+			    _cairo_fixed_integer_floor(box.p2.x) > rec_extents.x + rec_extents.width ||
 			    _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height)
 			{
 			    return CAIRO_INT_STATUS_UNSUPPORTED;
commit 063f9db67e2adc83c1be981de5fc2a6c78d101c2
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Jun 19 16:48:39 2022 +0930

    Fuzzer

diff --git a/meson-cc-tests/fuzzer.c b/meson-cc-tests/fuzzer.c
new file mode 100644
index 000000000..0ae4a3101
--- /dev/null
+++ b/meson-cc-tests/fuzzer.c
@@ -0,0 +1,7 @@
+#include <stddef.h>
+#include <stdint.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    return 0;
+}
diff --git a/test/svg/fuzzer/README b/test/svg/fuzzer/README
new file mode 100644
index 000000000..4bb8d9654
--- /dev/null
+++ b/test/svg/fuzzer/README
@@ -0,0 +1,19 @@
+libFuzzer based fuzzing for cairo-svg-glyph-render.c
+====================================================
+
+Build
+-----
+CC=clang CFLAGS="-DDEBUG_SVG_RENDER -g -fsanitize=fuzzer-no-link,address" meson -Db_lundef=false bld-fuzzer
+ninja -C bld-fuzzer
+
+
+Test
+----
+  ./bld-fuzzer/test/svg/fuzzer/svg-render-fuzzer <CORPUS DIR>
+
+where <CORPUS DIR> is a directory containing SVG files.
+
+If the fuzzer crashes, a crash-* file will be written. Run the
+fuzzer with the crash file to reproduce the crash.
+
+  ./bld-fuzzer/test/svg/fuzzer/svg-render-fuzzer <crash-file>
diff --git a/test/svg/fuzzer/meson.build b/test/svg/fuzzer/meson.build
new file mode 100644
index 000000000..37e23a474
--- /dev/null
+++ b/test/svg/fuzzer/meson.build
@@ -0,0 +1,14 @@
+fuzz_targets = [
+  'svg-render-fuzzer'
+]
+
+fuzz_args = ['-fsanitize=fuzzer,address']
+
+foreach target_name : fuzz_targets
+  exe = executable(target_name, [target_name + '.c'],
+  include_directories: [incbase, incsrc],
+  c_args: [fuzz_args, '-DHAVE_CONFIG_H'],
+  link_with: [libcairo],
+  link_args: [fuzz_args, extra_link_args],
+  dependencies: [deps, test_deps])
+endforeach
diff --git a/test/svg/fuzzer/svg-render-fuzzer.c b/test/svg/fuzzer/svg-render-fuzzer.c
new file mode 100644
index 000000000..08eb79dd8
--- /dev/null
+++ b/test/svg/fuzzer/svg-render-fuzzer.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2022 Uli Schlachter
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Uli Schlachter <psychon at znc.in>
+ */
+
+#include <cairo.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+cairo_bool_t
+_cairo_debug_svg_render (cairo_t       *cr,
+                         const char    *svg_document,
+                         const char    *element,
+                         double         units_per_em,
+                         int            debug_level);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    cairo_surface_t *s = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+    cairo_t *cr = cairo_create(s);
+
+    /* Get us a zero terminated string */
+    const char *svg_document = strndup ((const char *) data, size);
+
+    _cairo_debug_svg_render (cr,
+                             svg_document,
+                             NULL,
+                             1000,
+                             0);
+    free (svg_document);
+    cairo_destroy (cr);
+    cairo_surface_destroy (s);
+    return 0;
+}
diff --git a/test/svg/meson.build b/test/svg/meson.build
index b2b017d79..858e9d9cc 100644
--- a/test/svg/meson.build
+++ b/test/svg/meson.build
@@ -3,3 +3,7 @@ if librsvg_dep.found()
              'svg-render.c',
              dependencies: [libcairo_dep, librsvg_dep])
 endif
+
+if cc.links(files(meson.project_source_root() / 'meson-cc-tests/fuzzer.c'), args: '-fsanitize=fuzzer,address')
+  subdir('fuzzer')
+endif
commit 8233c6362bbe2ca2ac4d3ef3cfc79f15ee79bc40
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri Jun 24 06:44:30 2022 +0930

    ft-svg-render test

diff --git a/test/cairo-svg-test-doc.ttx b/test/cairo-svg-test-doc.ttx
new file mode 100644
index 000000000..c2cdb2817
--- /dev/null
+++ b/test/cairo-svg-test-doc.ttx
@@ -0,0 +1,689 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="zero"/>
+    <GlyphID id="2" name="one"/>
+    <GlyphID id="3" name="two"/>
+    <GlyphID id="4" name="three"/>
+    <GlyphID id="5" name="four"/>
+    <GlyphID id="6" name="five"/>
+    <GlyphID id="7" name="six"/>
+    <GlyphID id="8" name="seven"/>
+    <GlyphID id="9" name="eight"/>
+    <GlyphID id="10" name="nine"/>
+    <GlyphID id="11" name="A"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x6d925ed5"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 15 00:00:00 2022"/>
+    <modified value="Fri Jul  1 06:21:40 2022"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="1000"/>
+    <yMax value="1000"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1100"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="100"/>
+    <xMaxExtent value="1000"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="12"/>
+    <maxPoints value="4"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="256"/>
+    <maxSizeOfInstructions value="1"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="1000"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000001"/>
+    <ySubscriptXSize value="600"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="600"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="300"/>
+    <yStrikeoutSize value="0"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="djr "/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="48"/>
+    <usLastCharIndex value="65"/>
+    <sTypoAscender value="1000"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="720"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1100" lsb="0"/>
+    <mtx name="A" width="1100" lsb="0"/>
+    <mtx name="eight" width="1100" lsb="0"/>
+    <mtx name="five" width="1100" lsb="0"/>
+    <mtx name="four" width="1100" lsb="0"/>
+    <mtx name="nine" width="1100" lsb="0"/>
+    <mtx name="one" width="1100" lsb="0"/>
+    <mtx name="seven" width="1100" lsb="0"/>
+    <mtx name="six" width="1100" lsb="0"/>
+    <mtx name="three" width="1100" lsb="0"/>
+    <mtx name="two" width="1100" lsb="0"/>
+    <mtx name="zero" width="1100" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x34" name="four"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="five"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="six"/><!-- DIGIT SIX -->
+      <map code="0x37" name="seven"/><!-- DIGIT SEVEN -->
+      <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
+      <map code="0x39" name="nine"/><!-- DIGIT NINE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="A" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="eight" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="five" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="four" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="nine" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="seven" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="six" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="three" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="zero" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Doc
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Doc Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="2"/>
+    <palette index="0">
+      <color index="0" value="#C5A1D7FF"/>
+      <color index="1" value="#80DFC8FF"/>
+    </palette>
+    <palette index="1">
+      <color index="0" value="#6392A9FF"/>
+      <color index="1" value="#7896B3FF"/>
+    </palette>
+  </CPAL>
+
+  <SVG>
+
+    <svgDoc endGlyphID="0" startGlyphID="0">
+      <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"></svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="1" startGlyphID="1">
+      <![CDATA[<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
+  <rect x="10" y="-90" width="80" height="80"
+        fill="none" stroke="black" stroke-width="10"/>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="2" startGlyphID="2">
+      <![CDATA[<svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg">
+  <rect x="-4" y="-14" width="8" height="8"
+        fill="none" stroke="black" stroke-width="1"/>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="3" startGlyphID="3">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <image x="0" y="-1000" width="1000" height="1000"
+         xlink:href="data:image/png;base64,
+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
+BNcsrHnv+f9Qc51Rfhz5VAAAAABJRU5ErkJggg=="/>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="4" startGlyphID="4">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <image x="0" y="-1000" width="1000" height="1000"
+         transform="scale(0.5) rotate(-45),translate(800,500)"
+         xlink:href="data:image/png;base64,
+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
+BNcsrHnv+f9Qc51Rfhz5VAAAAABJRU5ErkJggg=="/>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="5" startGlyphID="5">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="userSpaceOnUse" >
+    <circle  cx="500" cy="-500" r="400" />
+  </clipPath>
+
+  <rect x="100" y="-900" width="800" height="800"
+        stroke="green" stroke-width="100"
+        clip-path="url(#clip)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="6" startGlyphID="6">
+      <![CDATA[<svg  xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="objectBoundingBox">
+    <circle cx=".5" cy=".5" r=".5" />
+  </clipPath>
+
+  <rect x="100" y="-900" width="800" height="800"
+        stroke="green" stroke-width="100"
+        clip-path="url(#clip)" />
+
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="7" startGlyphID="7">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="userSpaceOnUse" >
+    <circle  cx="500" cy="-500" r="400" />
+    <rect x="100" y="-900" width="800" height="400"/>
+  </clipPath>
+
+  <rect x="100" y="-900" width="800" height="800"
+        stroke="green" stroke-width="100"
+        clip-path="url(#clip)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="8" startGlyphID="8">
+      <![CDATA[<svg  xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="objectBoundingBox">
+    <circle cx=".5" cy=".5" r=".5" />
+    <rect x="0" y="0" width="1" height="0.5"/>
+  </clipPath>
+
+  <rect x="100" y="-900" width="800" height="800"
+        stroke="green" stroke-width="100"
+        clip-path="url(#clip)" />
+
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="9" startGlyphID="9">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="userSpaceOnUse" >
+    <circle  cx="500" cy="-500" r="400" />
+  </clipPath>
+
+  <clipPath id="clip2" clipPathUnits="userSpaceOnUse" >
+    <rect x="100" y="-900" width="800" height="400"/>
+  </clipPath>
+
+  <g clip-path="url(#clip)">
+     <rect x="100" y="-900" width="800" height="800"
+           stroke="green" stroke-width="100"
+           clip-path="url(#clip2)" />
+   </g>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="10" startGlyphID="10">
+      <![CDATA[<svg  xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="objectBoundingBox">
+    <circle cx=".5" cy=".5" r=".5" />
+  </clipPath>
+
+  <clipPath id="clip2" clipPathUnits="objectBoundingBox">
+    <rect x="0" y="0" width="1" height="0.5"/>
+  </clipPath>
+
+    <g clip-path="url(#clip)">
+      <rect x="100" y="-900" width="800" height="800"
+            stroke="green" stroke-width="100"
+            clip-path="url(#clip2)" />
+    </g>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="11" startGlyphID="11">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink" >
+  <defs>
+    <circle id="circle"
+            cx="0" cy="0" r="250" />
+  </defs>
+
+  <g opacity="0.5">
+    <use xlink:href="#circle" x="330" y="-500"
+         fill="red"  />
+    <use xlink:href="#circle" x="670" y="-500"
+         fill="green"  />
+  </g>
+</svg>]]>
+    </svgDoc>
+  </SVG>
+
+</ttFont>
diff --git a/test/cairo-svg-test-fill.ttx b/test/cairo-svg-test-fill.ttx
new file mode 100644
index 000000000..b6867ec8f
--- /dev/null
+++ b/test/cairo-svg-test-fill.ttx
@@ -0,0 +1,365 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="zero"/>
+    <GlyphID id="2" name="one"/>
+    <GlyphID id="3" name="two"/>
+    <GlyphID id="4" name="three"/>
+    <GlyphID id="5" name="four"/>
+    <GlyphID id="6" name="five"/>
+    <GlyphID id="7" name="six"/>
+    <GlyphID id="8" name="seven"/>
+    <GlyphID id="9" name="eight"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x71e2812"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 15 00:00:00 2022"/>
+    <modified value="Fri Jul  1 06:21:40 2022"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="1000"/>
+    <yMax value="1000"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1100"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="100"/>
+    <xMaxExtent value="1000"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="10"/>
+    <maxPoints value="4"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="256"/>
+    <maxSizeOfInstructions value="1"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="1000"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000001"/>
+    <ySubscriptXSize value="600"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="600"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="300"/>
+    <yStrikeoutSize value="0"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="djr "/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="48"/>
+    <usLastCharIndex value="56"/>
+    <sTypoAscender value="1000"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="720"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1100" lsb="0"/>
+    <mtx name="eight" width="1100" lsb="0"/>
+    <mtx name="five" width="1100" lsb="0"/>
+    <mtx name="four" width="1100" lsb="0"/>
+    <mtx name="one" width="1100" lsb="0"/>
+    <mtx name="seven" width="1100" lsb="0"/>
+    <mtx name="six" width="1100" lsb="0"/>
+    <mtx name="three" width="1100" lsb="0"/>
+    <mtx name="two" width="1100" lsb="0"/>
+    <mtx name="zero" width="1100" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x34" name="four"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="five"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="six"/><!-- DIGIT SIX -->
+      <map code="0x37" name="seven"/><!-- DIGIT SEVEN -->
+      <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="eight" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="five" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="four" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="seven" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="six" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="three" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="zero" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Fill
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Fill Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="2"/>
+    <palette index="0">
+      <color index="0" value="#C5A1D7FF"/>
+      <color index="1" value="#80DFC8FF"/>
+    </palette>
+    <palette index="1">
+      <color index="0" value="#6392A9FF"/>
+      <color index="1" value="#7896B3FF"/>
+    </palette>
+  </CPAL>
+
+  <SVG>
+
+    <svgDoc endGlyphID="0" startGlyphID="0">
+      <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"></svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="1" startGlyphID="1">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="indigo" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="2" startGlyphID="2">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="#AA55AA" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="3" startGlyphID="3">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="#A5A" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="4" startGlyphID="4">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="rgb (75, 0 , 130)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="5" startGlyphID="5">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="currentColor" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="6" startGlyphID="6">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="var(--color1, red)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="7" startGlyphID="7">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="magenta" fill-opacity="0.5" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="8" startGlyphID="8">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     color="green" >
+  <rect x="100" y="-750" width="800" height="500"
+        fill="currentColor" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="9" startGlyphID="9">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <g transform="scale(5,5) translate(0,-150)">
+    <polygon fill-rule="nonzero"
+             points="50,0 21,90 98,35 2,35 79,90"/>
+    <polygon fill-rule="evenodd"
+             points="150,0 121,90 198,35 102,35 179,90"/>
+  </g>
+</svg>]]>
+    </svgDoc>
+  </SVG>
+
+</ttFont>
diff --git a/test/cairo-svg-test-gradient.ttx b/test/cairo-svg-test-gradient.ttx
new file mode 100644
index 000000000..4a7a22175
--- /dev/null
+++ b/test/cairo-svg-test-gradient.ttx
@@ -0,0 +1,441 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="zero"/>
+    <GlyphID id="2" name="one"/>
+    <GlyphID id="3" name="two"/>
+    <GlyphID id="4" name="three"/>
+    <GlyphID id="5" name="four"/>
+    <GlyphID id="6" name="five"/>
+    <GlyphID id="7" name="six"/>
+    <GlyphID id="8" name="seven"/>
+    <GlyphID id="9" name="eight"/>
+    <GlyphID id="10" name="nine"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x4322c7de"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 15 00:00:00 2022"/>
+    <modified value="Fri Jul  1 06:21:39 2022"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="1000"/>
+    <yMax value="1000"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1100"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="100"/>
+    <xMaxExtent value="1000"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="11"/>
+    <maxPoints value="4"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="256"/>
+    <maxSizeOfInstructions value="1"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="1000"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000001"/>
+    <ySubscriptXSize value="600"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="600"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="300"/>
+    <yStrikeoutSize value="0"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="djr "/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="48"/>
+    <usLastCharIndex value="57"/>
+    <sTypoAscender value="1000"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="720"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1100" lsb="0"/>
+    <mtx name="eight" width="1100" lsb="0"/>
+    <mtx name="five" width="1100" lsb="0"/>
+    <mtx name="four" width="1100" lsb="0"/>
+    <mtx name="nine" width="1100" lsb="0"/>
+    <mtx name="one" width="1100" lsb="0"/>
+    <mtx name="seven" width="1100" lsb="0"/>
+    <mtx name="six" width="1100" lsb="0"/>
+    <mtx name="three" width="1100" lsb="0"/>
+    <mtx name="two" width="1100" lsb="0"/>
+    <mtx name="zero" width="1100" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x34" name="four"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="five"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="six"/><!-- DIGIT SIX -->
+      <map code="0x37" name="seven"/><!-- DIGIT SEVEN -->
+      <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
+      <map code="0x39" name="nine"/><!-- DIGIT NINE -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="eight" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="five" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="four" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="nine" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="seven" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="six" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="three" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="zero" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Gradient
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Gradient Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="2"/>
+    <palette index="0">
+      <color index="0" value="#C5A1D7FF"/>
+      <color index="1" value="#80DFC8FF"/>
+    </palette>
+    <palette index="1">
+      <color index="0" value="#6392A9FF"/>
+      <color index="1" value="#7896B3FF"/>
+    </palette>
+  </CPAL>
+
+  <SVG>
+
+    <svgDoc endGlyphID="0" startGlyphID="0">
+      <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"></svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="1" startGlyphID="1">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="grad" x1="33%" x21="66%">
+      <stop offset="0%" stop-color="blue" stop-opacity="1" />
+      <stop offset="100%" stop-color="red" stop-opacity="0.5" />
+    </linearGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="2" startGlyphID="2">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="grad" x1="33%" x2="66%"
+                    spreadMethod="reflect">
+      <stop offset="0%" stop-color="blue" stop-opacity="1" />
+      <stop offset="100%" stop-color="red" stop-opacity="1" />
+    </linearGradient>
+  </defs>
+  <rect x="0" y="-900" width="1000" height="800" fill="url(#grad)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="3" startGlyphID="3">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="grad" x1="33%" x2="66%"
+                    spreadMethod="repeat">
+      <stop offset="0%" stop-color="blue" stop-opacity="1" />
+      <stop offset="100%" stop-color="red" stop-opacity="1" />
+    </linearGradient>
+  </defs>
+  <rect x="0" y="-900" width="1000" height="800" fill="url(#grad)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="4" startGlyphID="4">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="grad" x1="300" y1="-500" x2="600" y2="-500"
+                    gradientUnits="userSpaceOnUse">
+      <stop offset="0%" stop-color="blue" stop-opacity="1" />
+      <stop offset="100%" stop-color="red" stop-opacity="1" />
+    </linearGradient>
+  </defs>
+  <rect x="0" y="-900" width="1000" height="800" fill="url(#grad)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="5" startGlyphID="5">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="grad" gradientTransform="rotate(45)">
+      <stop offset="0%" stop-color="blue" stop-opacity="1" />
+      <stop offset="100%" stop-color="red" stop-opacity="1" />
+    </linearGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="6" startGlyphID="6">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <radialGradient id="grad" cx="50%" cy="50%"
+                    fx="0.75" fy="0.35" r="0.5">
+      <stop offset="0%" stop-color="white" stop-opacity="1" />
+      <stop offset="100%" stop-color="green" stop-opacity="1" />
+    </radialGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="7" startGlyphID="7">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <radialGradient id="grad" cx="75%" cy="25%" r="33%"
+                    fx="0.64" fy="0.18" fr="0.17"
+                    spreadMethod="reflect" >
+      <stop offset="0%" stop-color="white" stop-opacity="1" />
+      <stop offset="100%" stop-color="green" stop-opacity="1" />
+    </radialGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="8" startGlyphID="8">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <radialGradient id="grad" cx="75%" cy="25%" r="33%"
+                    fx="0.64" fy="0.18" fr="0.17"
+                    spreadMethod="repeat" >
+      <stop offset="0%" stop-color="white" stop-opacity="1" />
+      <stop offset="100%" stop-color="green" stop-opacity="1" />
+    </radialGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="9" startGlyphID="9">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <radialGradient id="grad" cx="450" cy="-550" r="400"
+                    fx="600" fy="-400" fr="10"
+                    gradientUnits="userSpaceOnUse" >
+      <stop offset="0%" stop-color="red" stop-opacity="1" />
+      <stop offset="100%" stop-color="green" stop-opacity="0.5" />
+    </radialGradient>
+  </defs>
+  <rect x="0" y="-1000" width="1000" height="1000" fill="url(#grad)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="10" startGlyphID="10">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <radialGradient id="grad" cx="50%" cy="25%"
+                    fx="0.75" fy="0.35" r="0.5"
+                     gradientTransform="scale(1, 2)">
+      <stop offset="0%" stop-color="red" stop-opacity="1" />
+      <stop offset="100%" stop-color="green" stop-opacity="1" />
+    </radialGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>]]>
+    </svgDoc>
+  </SVG>
+
+</ttFont>
diff --git a/test/cairo-svg-test-path.ttx b/test/cairo-svg-test-path.ttx
new file mode 100644
index 000000000..9173a537d
--- /dev/null
+++ b/test/cairo-svg-test-path.ttx
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="zero"/>
+    <GlyphID id="2" name="one"/>
+    <GlyphID id="3" name="two"/>
+    <GlyphID id="4" name="three"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x20ff0b91"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 15 00:00:00 2022"/>
+    <modified value="Fri Jul  1 06:21:40 2022"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="1000"/>
+    <yMax value="1000"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1100"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="100"/>
+    <xMaxExtent value="1000"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="5"/>
+    <maxPoints value="4"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="256"/>
+    <maxSizeOfInstructions value="1"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="1000"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000001"/>
+    <ySubscriptXSize value="600"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="600"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="300"/>
+    <yStrikeoutSize value="0"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="djr "/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="48"/>
+    <usLastCharIndex value="51"/>
+    <sTypoAscender value="1000"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="720"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1100" lsb="0"/>
+    <mtx name="one" width="1100" lsb="0"/>
+    <mtx name="three" width="1100" lsb="0"/>
+    <mtx name="two" width="1100" lsb="0"/>
+    <mtx name="zero" width="1100" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="one" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="three" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="zero" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Path
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Path Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="2"/>
+    <palette index="0">
+      <color index="0" value="#C5A1D7FF"/>
+      <color index="1" value="#80DFC8FF"/>
+    </palette>
+    <palette index="1">
+      <color index="0" value="#6392A9FF"/>
+      <color index="1" value="#7896B3FF"/>
+    </palette>
+  </CPAL>
+
+  <SVG>
+
+    <svgDoc endGlyphID="0" startGlyphID="0">
+      <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"></svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="1" startGlyphID="1">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <path fill="none" stroke="black" stroke-width="20"
+        d="M 200 -200 
+           L 500 -500
+           H 800
+           V -200
+  " />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="2" startGlyphID="2">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <path fill="none" stroke="black" stroke-width="20"
+        d="M200,-400
+           C200,-200
+           500,-200
+           500,-400
+           S800,-600
+           800,-400" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="3" startGlyphID="3">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <path fill="none" stroke="black" stroke-width="20"
+        d="M200,-300
+           Q400,-50
+           600,-300
+           T1000,-300"
+        />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="4" startGlyphID="4">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <path fill="none" stroke="black" stroke-width="20"
+        d="M500,-500
+           L 712 -712
+           A300,300 0 1,0 712, -288 z"
+        />
+</svg>]]>
+    </svgDoc>
+  </SVG>
+
+</ttFont>
diff --git a/test/cairo-svg-test-shapes.ttx b/test/cairo-svg-test-shapes.ttx
new file mode 100644
index 000000000..15a573a64
--- /dev/null
+++ b/test/cairo-svg-test-shapes.ttx
@@ -0,0 +1,333 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="zero"/>
+    <GlyphID id="2" name="one"/>
+    <GlyphID id="3" name="two"/>
+    <GlyphID id="4" name="three"/>
+    <GlyphID id="5" name="four"/>
+    <GlyphID id="6" name="five"/>
+    <GlyphID id="7" name="six"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xa8919a30"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 15 00:00:00 2022"/>
+    <modified value="Fri Jul  1 06:21:40 2022"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="1000"/>
+    <yMax value="1000"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1100"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="100"/>
+    <xMaxExtent value="1000"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="4"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="256"/>
+    <maxSizeOfInstructions value="1"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="1000"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000001"/>
+    <ySubscriptXSize value="600"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="600"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="300"/>
+    <yStrikeoutSize value="0"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="djr "/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="48"/>
+    <usLastCharIndex value="54"/>
+    <sTypoAscender value="1000"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="720"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1100" lsb="0"/>
+    <mtx name="five" width="1100" lsb="0"/>
+    <mtx name="four" width="1100" lsb="0"/>
+    <mtx name="one" width="1100" lsb="0"/>
+    <mtx name="six" width="1100" lsb="0"/>
+    <mtx name="three" width="1100" lsb="0"/>
+    <mtx name="two" width="1100" lsb="0"/>
+    <mtx name="zero" width="1100" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x34" name="four"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="five"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="six"/><!-- DIGIT SIX -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="five" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="four" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="six" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="three" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="zero" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Shapes
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Shapes Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="2"/>
+    <palette index="0">
+      <color index="0" value="#C5A1D7FF"/>
+      <color index="1" value="#80DFC8FF"/>
+    </palette>
+    <palette index="1">
+      <color index="0" value="#6392A9FF"/>
+      <color index="1" value="#7896B3FF"/>
+    </palette>
+  </CPAL>
+
+  <SVG>
+
+    <svgDoc endGlyphID="0" startGlyphID="0">
+      <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"></svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="1" startGlyphID="1">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"/>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="2" startGlyphID="2">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500" rx="100" ry="100"/>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="3" startGlyphID="3">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <circle cx="500" cy="-500" r="400"/>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="4" startGlyphID="4">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <ellipse cx="500" cy="-500" rx="400" ry="200"/>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="5" startGlyphID="5">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="200" y1="-200" x2="800" y2="-800" stroke="black" stroke-width="20"/>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="6" startGlyphID="6">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <polyline fill="none" stroke="black" stroke-width="20"
+            points="100, -100,
+                    300, -100,
+                    300, -300,
+                    500, -300,
+                    500, -500,
+                    700, -500
+                    700, -700
+                    900, -700
+                    900, -900" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="7" startGlyphID="7">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<polygon points="350, -75
+                 379, -161
+                 469, -161
+                 397, -215
+                 423, -301
+                 350, -250
+                 277, -301
+                 303, -215
+                 231, -161
+                 321, -161" />
+</svg>]]>
+    </svgDoc>
+  </SVG>
+
+</ttFont>
diff --git a/test/cairo-svg-test-stroke.ttx b/test/cairo-svg-test-stroke.ttx
new file mode 100644
index 000000000..a403da928
--- /dev/null
+++ b/test/cairo-svg-test-stroke.ttx
@@ -0,0 +1,608 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="zero"/>
+    <GlyphID id="2" name="one"/>
+    <GlyphID id="3" name="two"/>
+    <GlyphID id="4" name="three"/>
+    <GlyphID id="5" name="four"/>
+    <GlyphID id="6" name="five"/>
+    <GlyphID id="7" name="six"/>
+    <GlyphID id="8" name="seven"/>
+    <GlyphID id="9" name="eight"/>
+    <GlyphID id="10" name="nine"/>
+    <GlyphID id="11" name="A"/>
+    <GlyphID id="12" name="B"/>
+    <GlyphID id="13" name="C"/>
+    <GlyphID id="14" name="D"/>
+    <GlyphID id="15" name="E"/>
+    <GlyphID id="16" name="F"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x2d1b9ede"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 15 00:00:00 2022"/>
+    <modified value="Fri Jul  1 06:21:39 2022"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="1000"/>
+    <yMax value="1000"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1100"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="100"/>
+    <xMaxExtent value="1000"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="17"/>
+    <maxPoints value="4"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="256"/>
+    <maxSizeOfInstructions value="1"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="1000"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000001"/>
+    <ySubscriptXSize value="600"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="600"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="300"/>
+    <yStrikeoutSize value="0"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="djr "/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="48"/>
+    <usLastCharIndex value="70"/>
+    <sTypoAscender value="1000"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="720"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1100" lsb="0"/>
+    <mtx name="A" width="1100" lsb="0"/>
+    <mtx name="B" width="1100" lsb="0"/>
+    <mtx name="C" width="1100" lsb="0"/>
+    <mtx name="D" width="1100" lsb="0"/>
+    <mtx name="E" width="1100" lsb="0"/>
+    <mtx name="F" width="1100" lsb="0"/>
+    <mtx name="eight" width="1100" lsb="0"/>
+    <mtx name="five" width="1100" lsb="0"/>
+    <mtx name="four" width="1100" lsb="0"/>
+    <mtx name="nine" width="1100" lsb="0"/>
+    <mtx name="one" width="1100" lsb="0"/>
+    <mtx name="seven" width="1100" lsb="0"/>
+    <mtx name="six" width="1100" lsb="0"/>
+    <mtx name="three" width="1100" lsb="0"/>
+    <mtx name="two" width="1100" lsb="0"/>
+    <mtx name="zero" width="1100" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x34" name="four"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="five"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="six"/><!-- DIGIT SIX -->
+      <map code="0x37" name="seven"/><!-- DIGIT SEVEN -->
+      <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
+      <map code="0x39" name="nine"/><!-- DIGIT NINE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+      <map code="0x45" name="E"/><!-- LATIN CAPITAL LETTER E -->
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="A" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="B" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="C" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="D" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="E" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="eight" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="five" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="four" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="nine" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="seven" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="six" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="three" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="zero" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Stroke
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Stroke Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="2"/>
+    <palette index="0">
+      <color index="0" value="#C5A1D7FF"/>
+      <color index="1" value="#80DFC8FF"/>
+    </palette>
+    <palette index="1">
+      <color index="0" value="#6392A9FF"/>
+      <color index="1" value="#7896B3FF"/>
+    </palette>
+  </CPAL>
+
+  <SVG>
+
+    <svgDoc endGlyphID="0" startGlyphID="0">
+      <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"></svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="1" startGlyphID="1">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="indigo" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="2" startGlyphID="2">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="#AA55AA" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="3" startGlyphID="3">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="#A5A" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="4" startGlyphID="4">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="rgb(75,0,130)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="5" startGlyphID="5">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="currentColor" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="6" startGlyphID="6">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="var(--color1, red)" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="7" startGlyphID="7">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="magenta" stroke-opacity="0.5" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="8" startGlyphID="8">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     color="green" >
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="currentColor" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="9" startGlyphID="9">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-900" x2="900" y2="-900"
+        fill="none"
+        stroke-width="100"
+        stroke="black" />
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="200"
+        stroke="black" />
+  <line x1="100" y1="-100" x2="900" y2="-100"
+        fill="none"
+        stroke-width="300"
+        stroke="black" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="10" startGlyphID="10">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-900" x2="900" y2="-900"
+        fill="none"
+        stroke-width="200"
+        stroke-linecap="butt"
+        stroke="black" />
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="200"
+        stroke-linecap="round"
+        stroke="black" />
+  <line x1="100" y1="-100" x2="900" y2="-100"
+        fill="none"
+        stroke-width="200"
+        stroke-linecap="square"
+        stroke="black" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="11" startGlyphID="11">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-950" x2="900" y2="-950"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="none"
+        stroke="black" />
+  <line x1="100" y1="-750" x2="900" y2="-750"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200"
+        stroke="black" />
+  <line x1="100" y1="-550" x2="900" y2="-550"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50"
+        stroke="black" />
+  <line x1="100" y1="-350" x2="900" y2="-350"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50 100"
+        stroke="black" />
+  <line x1="100" y1="-150" x2="900" y2="-150"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50 100 150"
+        stroke="black" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="12" startGlyphID="12">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-950" x2="900" y2="-950"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="none"
+        stroke="black" />
+  <line x1="100" y1="-750" x2="900" y2="-750"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200"
+        stroke-dashoffset="100"
+        stroke="black" />
+  <line x1="100" y1="-550" x2="900" y2="-550"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50"
+        stroke-dashoffset="100"
+        stroke="black" />
+  <line x1="100" y1="-350" x2="900" y2="-350"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50 100"
+        stroke-dashoffset="100"
+        stroke="black" />
+  <line x1="100" y1="-150" x2="900" y2="-150"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50 100 150"
+        stroke-dashoffset="100"
+        stroke="black" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="13" startGlyphID="13">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <polyline
+      points="100, -200
+              900, -400
+              100, -700"
+      fill="none"
+      stroke-width="200"
+      stroke-linejoin="miter"
+      stroke="black" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="14" startGlyphID="14">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <polyline
+      points="100, -200
+              900, -400
+              100, -700"
+      fill="none"
+      stroke-width="200"
+      stroke-linejoin="round"
+      stroke="black" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="15" startGlyphID="15">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <polyline
+      points="100, -200
+              900, -400
+              100, -700"
+      fill="none"
+      stroke-width="200"
+      stroke-linejoin="bevel"
+      stroke="black" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="16" startGlyphID="16">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <polyline
+      points="100, -200
+              900, -400
+              100, -700"
+      fill="none"
+      stroke-width="200"
+      stroke-linejoin="miter"
+      stroke-miterlimit="2"
+      stroke="black" />
+</svg>]]>
+    </svgDoc>
+  </SVG>
+
+</ttFont>
diff --git a/test/cairo-svg-test-transform.ttx b/test/cairo-svg-test-transform.ttx
new file mode 100644
index 000000000..1f07c05ad
--- /dev/null
+++ b/test/cairo-svg-test-transform.ttx
@@ -0,0 +1,403 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="zero"/>
+    <GlyphID id="2" name="one"/>
+    <GlyphID id="3" name="two"/>
+    <GlyphID id="4" name="three"/>
+    <GlyphID id="5" name="four"/>
+    <GlyphID id="6" name="five"/>
+    <GlyphID id="7" name="six"/>
+    <GlyphID id="8" name="seven"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x970fff1"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 15 00:00:00 2022"/>
+    <modified value="Fri Jul  1 06:21:39 2022"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="1000"/>
+    <yMax value="1000"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1100"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="100"/>
+    <xMaxExtent value="1000"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="9"/>
+    <maxPoints value="4"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="256"/>
+    <maxSizeOfInstructions value="1"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="1000"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000001"/>
+    <ySubscriptXSize value="600"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="600"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="300"/>
+    <yStrikeoutSize value="0"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="djr "/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="48"/>
+    <usLastCharIndex value="55"/>
+    <sTypoAscender value="1000"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="720"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1100" lsb="0"/>
+    <mtx name="five" width="1100" lsb="0"/>
+    <mtx name="four" width="1100" lsb="0"/>
+    <mtx name="one" width="1100" lsb="0"/>
+    <mtx name="seven" width="1100" lsb="0"/>
+    <mtx name="six" width="1100" lsb="0"/>
+    <mtx name="three" width="1100" lsb="0"/>
+    <mtx name="two" width="1100" lsb="0"/>
+    <mtx name="zero" width="1100" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x34" name="four"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="five"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="six"/><!-- DIGIT SIX -->
+      <map code="0x37" name="seven"/><!-- DIGIT SEVEN -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="five" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="four" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="seven" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="six" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="three" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="zero" xMin="0" yMin="0" xMax="1000" yMax="1000">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Transform
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Cairo Svg Test Transform Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="2"/>
+    <palette index="0">
+      <color index="0" value="#C5A1D7FF"/>
+      <color index="1" value="#80DFC8FF"/>
+    </palette>
+    <palette index="1">
+      <color index="0" value="#6392A9FF"/>
+      <color index="1" value="#7896B3FF"/>
+    </palette>
+  </CPAL>
+
+  <SVG>
+
+    <svgDoc endGlyphID="0" startGlyphID="0">
+      <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"></svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="1" startGlyphID="1">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <use xlink:href="#square" transform="translate(600, -400)"/>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="2" startGlyphID="2">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <g transform="translate(400, -400)">
+    <use xlink:href="#square" transform="scale(1.5, 0.5)"/>
+  </g>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="3" startGlyphID="3">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <g transform="translate(600, -600)">
+    <use xlink:href="#square" transform="rotate(30)"/>
+  </g>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="4" startGlyphID="4">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <g transform="translate(400, -400)">
+    <use xlink:href="#square" transform="skewX(30)"/>
+  </g>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="5" startGlyphID="5">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <g transform="translate(400, -500)">
+    <use xlink:href="#square" transform="skewY(30)"/>
+  </g>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="6" startGlyphID="6">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <g transform="translate(600, -600)">
+    <use xlink:href="#square" transform="matrix(1, 0.4, -0.6, 1.1, 50, -70)"/>
+  </g>
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="7" startGlyphID="7">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <path id="heart" d="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z" />
+  </defs>
+  <use xlink:href="#heart"
+       transform="translate(10, -650)
+                  rotate(-10 50 100)
+                  translate(-166 125.5)
+                  skewX(40)
+                  scale(1 0.5),scale(8, 8)"
+       fill="grey"/>
+  <use xlink:href="#heart"
+       transform="translate(300, -800),scale(6, 6)"
+       fill="none" stroke="red" />
+</svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="8" startGlyphID="8">
+      <![CDATA[<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <circle id="c1"
+            cx="100" cy="100" r="80"
+            fill="none"
+            stroke="black"
+            stroke-width="20"  />
+    <circle id="c2"
+          cx="100" cy="100" r="80"
+          fill="none"
+          stroke="black"
+          stroke-width="20"
+          transform="scale(3,3)" />
+  </defs>
+
+  <use xlink:href="#c1" x="100" y="-900"/>
+  <use xlink:href="#c2" x="300" y="-700"/>
+  
+</svg>]]>
+    </svgDoc>
+  </SVG>
+
+</ttFont>
diff --git a/test/ft-svg-render.c b/test/ft-svg-render.c
new file mode 100644
index 000000000..9b0351b10
--- /dev/null
+++ b/test/ft-svg-render.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright © 2022 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairo-test.h"
+#include <cairo-ft.h>
+
+#define GLYPH_SIZE 50
+#define PAD 5
+#define WIDTH  (4*(GLYPH_SIZE + PAD) + PAD)
+#define HEIGHT WIDTH
+
+//#define CLIP 1
+#define LOG_EXTENTS 1
+
+static cairo_test_status_t
+draw_font (cairo_t *cr, int width, int height, const char *font_file)
+{
+    cairo_test_status_t result;
+    char buf[10];
+    cairo_text_extents_t extents;
+    cairo_font_options_t *font_options;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    result = cairo_test_ft_select_font_from_file (cr, font_file);
+    if (result)
+        return result;
+
+    font_options = cairo_font_options_create ();
+    cairo_font_options_set_color_mode (font_options, CAIRO_COLOR_MODE_NO_COLOR);
+//    cairo_set_font_options (cr, font_options);
+    cairo_font_options_destroy (font_options);
+
+    cairo_set_font_size (cr, GLYPH_SIZE);
+    for (int i = 0; i < 4; i++) {
+        for (int j = 0; j < 4; j++) {
+            int x = j * (GLYPH_SIZE + PAD) + PAD;
+            int y = i * (GLYPH_SIZE + PAD) + PAD;
+            int glyph_number = 4*i + j;
+            buf[0] = glyph_number < 10 ? '0' + glyph_number : 'A' + glyph_number - 10;
+            buf[1] = 0;
+            cairo_save (cr);
+            cairo_text_extents (cr, buf, &extents);
+#if LOG_EXTENTS
+            cairo_test_log (cairo_test_get_context (cr),
+                            "Char '%c' extents: x_bearing: %f  y_bearing: %f  width: %f  height: %f  x_advance: %f  y_advance: %f\n",
+                            buf[0],
+                            extents.x_bearing,
+                            extents.y_bearing,
+                            extents.width,
+                            extents.height,
+                            extents.x_advance,
+                            extents.y_advance);
+#endif
+#if CLIP
+            cairo_rectangle (cr, x, y, GLYPH_SIZE, GLYPH_SIZE);
+            cairo_clip (cr);
+#endif
+            cairo_move_to (cr, x, y + GLYPH_SIZE);
+            cairo_show_text (cr, buf);
+            cairo_restore (cr);
+            if (cairo_status (cr)) {
+                cairo_test_log (cairo_test_get_context (cr),
+                                "cairo_show_text() failed with \"%s\"\n",
+                                buf);
+                return CAIRO_TEST_FAILURE;
+            }
+        }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+#define DRAW_FUNC(name) \
+static cairo_test_status_t \
+draw_##name (cairo_t *cr, int width, int height) { \
+    return draw_font (cr, width, height, "cairo-svg-test-" #name ".ttf"); \
+}
+
+DRAW_FUNC(doc)
+CAIRO_TEST (ft_svg_render_doc,
+	    "Test SVG glyph render",
+	    "svgrender", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw_doc)
+
+DRAW_FUNC(fill)
+CAIRO_TEST (ft_svg_render_fill,
+	    "Test SVG glyph render",
+	    "svgrender", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw_fill)
+    
+DRAW_FUNC(gradient)
+CAIRO_TEST (ft_svg_render_gradient,
+	    "Test SVG glyph render",
+	    "svgrender", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw_gradient)
+    
+DRAW_FUNC(path)
+CAIRO_TEST (ft_svg_render_path,
+	    "Test SVG glyph render",
+	    "svgrender", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw_path)
+    
+DRAW_FUNC(shapes)
+CAIRO_TEST (ft_svg_render_shapes,
+	    "Test SVG glyph render",
+	    "svgrender", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw_shapes)
+
+DRAW_FUNC(stroke)
+CAIRO_TEST (ft_svg_render_stroke,
+	    "Test SVG glyph render",
+	    "svgrender", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw_stroke)
+
+DRAW_FUNC(transform)
+CAIRO_TEST (ft_svg_render_transform,
+	    "Test SVG glyph render",
+	    "svgrender", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw_transform)
diff --git a/test/meson.build b/test/meson.build
index 3071ac6ca..3606bfc27 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -431,6 +431,7 @@ test_ft_svg_font_sources = [
 
 test_ft_svg_ttx_font_sources = [
   'ft-svg-cairo-logo.c',
+  'ft-svg-render.c'
 ]
 
 test_gl_sources = [
@@ -525,6 +526,13 @@ ps2png_sources = [
 
 test_ttx_fonts = [
   'cairo-logo-font.ttx',
+  'cairo-svg-test-doc.ttx',
+  'cairo-svg-test-fill.ttx',
+  'cairo-svg-test-gradient.ttx',
+  'cairo-svg-test-path.ttx',
+  'cairo-svg-test-shapes.ttx',
+  'cairo-svg-test-stroke.ttx',
+  'cairo-svg-test-transform.ttx',
 ]
 
 build_any2ppm = false
@@ -681,3 +689,9 @@ test('cairo', exe,
   suite: ['cairo-test-suite', 'slow'],
   workdir: meson.current_build_dir(),
   depends: test_depends)
+
+# The SVG renderer debug tools can only be built if the _cairo_debug_svg_render()
+# function has been exposed by defining DEBUG_SVG_RENDER
+if conf.get('HAVE_FT_SVG_DOCUMENT', 0) == 1 and cc.get_define('DEBUG_SVG_RENDER') != ''
+  subdir('svg')
+endif
diff --git a/test/reference/ft-svg-render-doc.ref.png b/test/reference/ft-svg-render-doc.ref.png
new file mode 100644
index 000000000..e7ecf7f1a
Binary files /dev/null and b/test/reference/ft-svg-render-doc.ref.png differ
diff --git a/test/reference/ft-svg-render-fill.ref.png b/test/reference/ft-svg-render-fill.ref.png
new file mode 100644
index 000000000..a72cdd1f2
Binary files /dev/null and b/test/reference/ft-svg-render-fill.ref.png differ
diff --git a/test/reference/ft-svg-render-gradient.ref.png b/test/reference/ft-svg-render-gradient.ref.png
new file mode 100644
index 000000000..d647dc06a
Binary files /dev/null and b/test/reference/ft-svg-render-gradient.ref.png differ
diff --git a/test/reference/ft-svg-render-path.ref.png b/test/reference/ft-svg-render-path.ref.png
new file mode 100644
index 000000000..2667059cc
Binary files /dev/null and b/test/reference/ft-svg-render-path.ref.png differ
diff --git a/test/reference/ft-svg-render-shapes.ref.png b/test/reference/ft-svg-render-shapes.ref.png
new file mode 100644
index 000000000..209e0ddb3
Binary files /dev/null and b/test/reference/ft-svg-render-shapes.ref.png differ
diff --git a/test/reference/ft-svg-render-stroke.ref.png b/test/reference/ft-svg-render-stroke.ref.png
new file mode 100644
index 000000000..86f1ab9b6
Binary files /dev/null and b/test/reference/ft-svg-render-stroke.ref.png differ
diff --git a/test/reference/ft-svg-render-transform.ref.png b/test/reference/ft-svg-render-transform.ref.png
new file mode 100644
index 000000000..dcc372aac
Binary files /dev/null and b/test/reference/ft-svg-render-transform.ref.png differ
diff --git a/test/svg/README b/test/svg/README
new file mode 100644
index 000000000..c4504dc9b
--- /dev/null
+++ b/test/svg/README
@@ -0,0 +1,22 @@
+build_ttx_fonts.py
+==================
+Is used to create the test/*.ttx files used for testing the SVG glyph renderer.
+
+build_ttx_fonts.py will look for files of the form
+
+<font-name>.<char>.<test-name>.svg
+
+in the input directory, and using svg-font-template.ttx, create <font-name>.ttx
+files that contain one glyph for each svg file. Each <font-name>.ttx will contain
+a glyph for each svg with the matching <font-name> prefix. Each glyphs will be mapped
+to the <char> i nthe svg filename. The <char> must be one of 0-9,A-F.
+
+svg-render.c
+============
+svg-render renders SVG files using both librsvg and cairo-svg-glyph-render.c.
+It is used for testing cairo-svg-glyph-render.c during development.
+
+To use svg-render, cairo must be built with CFLAGS="-DDEBUG_SVG_RENDER" to enable the
+_cairo_debug_svg_render() function.
+
+
diff --git a/test/svg/build_ttx_fonts.py b/test/svg/build_ttx_fonts.py
new file mode 100755
index 000000000..d7e7f5b8f
--- /dev/null
+++ b/test/svg/build_ttx_fonts.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python3
+
+# Build the ttx cairo svg test fonts from svg files
+# The svg files use the naming convention
+#  <font-name>.<char>.<test-name>.svg
+# eg "circle.A.cx_cy_r.svg"
+#
+# <font-name> is use to create the name of the ttx font.
+# <char> is a ascii hex character (uppercase) that the font fill map to the SVG description.
+# <test-name> is a descriptive name of the SVG file and is not used to build the font…
+#
+# This script looks for all files matching the above pattern and
+# creates one ttx font for each unique <font-name>. Each font will
+# contain up to 16 characters. The SVG description of each character
+# and the character that maps to the SVG description is obtained from
+# the SVG file beginning with <font-name>.<char>.
+
+import argparse
+import os
+import re
+import sys
+import xml.dom.minidom
+
+TEMPLATE_FILE="svg-font-template.ttx"
+
+glyph_names = {
+    '0': 'zero',
+    '1': 'one',
+    '2': 'two',
+    '3': 'three',
+    '4': 'four',
+    '5': 'five',
+    '6': 'six',
+    '7': 'seven',
+    '8': 'eight',
+    '9': 'nine',
+    'A': 'A',
+    'B': 'B',
+    'C': 'C',
+    'D': 'D',
+    'E': 'E',
+    'F': 'F'
+}
+
+# files is list of (char, filename)
+def build_font(font_name, files, in_dir, out_dir, no_reformat):
+    name = "cairo-svg-test-" + font_name
+    doc = xml.dom.minidom.parse(os.path.join(in_dir, TEMPLATE_FILE))
+    glyph_id = 1
+    text_nl = doc.createTextNode('\n\n')
+    for f in sorted(files):
+        glyph_name = glyph_names[f[0]]
+
+        glyph_order = doc.getElementsByTagName('GlyphOrder')[0]
+        glyph_id_elem = doc.createElement('GlyphID')
+        glyph_id_elem.setAttribute('id', str(glyph_id))
+        glyph_id_elem.setAttribute('name', glyph_name)
+        glyph_order.appendChild(glyph_id_elem)
+        glyph_order.appendChild(text_nl)
+
+        hmtx = doc.getElementsByTagName('hmtx')[0]
+        mtx = doc.createElement('mtx')
+        mtx.setAttribute('name', glyph_name)
+        mtx.setAttribute('width', '1100')
+        mtx.setAttribute('lsb', '0')
+        hmtx.appendChild(mtx)
+
+        cmap_format = doc.getElementsByTagName('cmap_format_4')[0]
+        map = doc.createElement('map')
+        map.setAttribute('code', hex(ord(f[0])))
+        map.setAttribute('name', glyph_name)
+        cmap_format.appendChild(map)
+
+        glyf = doc.getElementsByTagName('glyf')[0]
+        tt_glyph = doc.createElement('TTGlyph')
+        tt_glyph.setAttribute('name', glyph_name)
+        glyf.appendChild(tt_glyph)
+        contour = doc.createElement('contour')
+        tt_glyph.appendChild(contour)
+        pt = doc.createElement('pt')
+        pt.setAttribute('x', "0")
+        pt.setAttribute('y', "0")
+        pt.setAttribute('on', "1")
+        contour.appendChild(pt)
+        pt = doc.createElement('pt')
+        pt.setAttribute('x', "0")
+        pt.setAttribute('y', "1000")
+        pt.setAttribute('on', "1")
+        contour.appendChild(pt)
+        pt = doc.createElement('pt')
+        pt.setAttribute('x', "1000")
+        pt.setAttribute('y', "1000")
+        pt.setAttribute('on', "1")
+        contour.appendChild(pt)
+        pt = doc.createElement('pt')
+        pt.setAttribute('x', "1000")
+        pt.setAttribute('y', "0")
+        pt.setAttribute('on', "1")
+        contour.appendChild(pt)
+        instructions = doc.createElement('instructions')
+        tt_glyph.appendChild(instructions)
+
+        svg = doc.getElementsByTagName('SVG')[0]
+        svgdoc = doc.createElement('svgDoc')
+        svgdoc.setAttribute('startGlyphID', str(glyph_id))
+        svgdoc.setAttribute('endGlyphID', str(glyph_id))
+        with open(os.path.join(in_dir, f[1]), 'r') as svg_file:
+            svg_data = svg_file.read()
+        cdata = doc.createCDATASection(svg_data)
+        svgdoc.appendChild(cdata)
+        svg.appendChild(svgdoc)
+        glyph_id += 1
+
+    name_record = doc.getElementsByTagName('namerecord')[0]
+    name_record.firstChild.replaceWholeText(name.replace("-", " ").title())
+    name_record = doc.getElementsByTagName('namerecord')[2]
+    name_record.firstChild.replaceWholeText(name.replace("-", " ").title() + " Regular")
+
+    ttx_filename = os.path.join(out_dir, name + '.ttx')
+    ttf_filename = os.path.join(out_dir, name + '.ttf')
+    with open(ttx_filename, 'w') as ttx_file:
+        doc.writexml(ttx_file)
+
+    if not no_reformat:
+        # Convert to ttf and back to ttx. This reformats the ttx file
+        # which allows better quality diffs.
+        if os.path.exists(ttf_filename):
+            os.remove(ttf_filename)
+        os.system("ttx " + ttx_filename)
+        os.remove(ttx_filename)
+        os.system("ttx " + ttf_filename)
+        os.remove(ttf_filename)
+
+def build_file_list(in_dir):
+    dict = {}
+    regex_prog = re.compile(r"([^\.]+)\.(.)\.[^\.]+\.svg", re.ASCII)
+    files = os.listdir(in_dir)
+    for f in files:
+        match = regex_prog.fullmatch(f)
+        if match:
+            fontname = match.group(1)
+            character = match.group(2)
+            if (fontname not in dict):
+                dict[fontname] = [(character, f)];
+            else:
+                dict[fontname].append((character, f))
+    return dict
+
+if __name__=='__main__':
+    parser = argparse.ArgumentParser(description='Build ttx fonts.')
+    parser.add_argument("-i", nargs=1, metavar="indir", default=["."], help="Input directory")
+    parser.add_argument("-o", nargs=1, metavar="outdir", default=["."], help="Output directory")
+    parser.add_argument("-n", action='store_true', help="Don't reformat the output.")
+    args = parser.parse_args()
+    in_dir = args.i[0]
+    out_dir = args.o[0]
+    no_reformat = args.n
+    file_list = build_file_list(in_dir)
+    font_name = None
+    for key, value in file_list.items():
+        build_font(key, value, in_dir, out_dir, no_reformat)
diff --git a/test/svg/doc.0.viewBox1.svg b/test/svg/doc.0.viewBox1.svg
new file mode 100644
index 000000000..c1f55a4eb
--- /dev/null
+++ b/test/svg/doc.0.viewBox1.svg
@@ -0,0 +1,4 @@
+<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
+  <rect x="10" y="-90" width="80" height="80"
+        fill="none" stroke="black" stroke-width="10"/>
+</svg>
diff --git a/test/svg/doc.1.viewBox2.svg b/test/svg/doc.1.viewBox2.svg
new file mode 100644
index 000000000..b8f976ba9
--- /dev/null
+++ b/test/svg/doc.1.viewBox2.svg
@@ -0,0 +1,4 @@
+<svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg">
+  <rect x="-4" y="-14" width="8" height="8"
+        fill="none" stroke="black" stroke-width="1"/>
+</svg>
diff --git a/test/svg/doc.2.image.svg b/test/svg/doc.2.image.svg
new file mode 100644
index 000000000..1d03842a9
--- /dev/null
+++ b/test/svg/doc.2.image.svg
@@ -0,0 +1,121 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <image x="0" y="-1000" width="1000" height="1000"
+         xlink:href="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9
+kT1Iw0AYht+m1opUHOwgopChOlkQFXXUKhShQqkVWnUwufQPmjQkKS6OgmvBwZ/FqoOLs64OroIg
++APi6OSk6CIlfpcUWsR4x3EP733vy913gFAvM9XsGANUzTJS8ZiYya6KwVd0YggBmtMSM/W5ZDIB
+z/F1Dx/f76I8y7vuz9Gj5EwG+ETiWaYbFvEG8dSmpXPeJw6zoqQQnxOPGnRB4keuyy6/cS44LPDM
+sJFOzROHicVCG8ttzIqGSjxJHFFUjfKFjMsK5y3OarnKmvfkLwzltJVlrtMaRByLWEISImRUUUIZ
+FqK0a6SYSNF5zMM/4PiT5JLJVQIjxwIqUCE5fvA/+N1bMz8x7iaFYkDgxbY/hoHgLtCo2fb3sW03
+TgD/M3CltfyVOjDzSXqtpUWOgN5t4OK6pcl7wOUO0P+kS4bkSH5aQj4PvJ/RN2WBvluge83tW/Mc
+pw9AmnqVuAEODoGRAmWve7y7q71v/9Y0+/cDaP1yo4gSHTAAAAAGYktHRAD/AP8A/6C9p5MAAAAJ
+cEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfmBh0FBzE5rnzLAAAAGXRFWHRDb21tZW50AENyZWF0
+ZWQgd2l0aCBHSU1QV4EOFwAAF45JREFUaN5NmnmPZOd13n/vdrdau6p6X2cjw5FIarMjAYmDwP7H
+Buz4K/ib8IMFQaAgtiVLoRRSociZIWc4Pb1Xda237vIu+eNWD91AoYHuu9R7luc85zlHfPyzvwxC
+CJRSzUdKpJAIKVBSIZVESoGUGqkkQggIASkE2miMiTGRQWmNkAqlNVobBMDmPgApwVmHrWu894QQ
+CCHgvcMHh6stdVVh6xpra5xzeO/xLry/NtBcj5AIIRFSEbzFe49+OIQQAgDnPSiBbL4KUki0Nmhj
+EFIhmz8jCGhtMFHzP6kNSjbPkVJuDNC88OGLSCnRujlYAELwBB/w3uOURWlFXSlUrTYHCVhrsdbi
+nYMQmpcTCMGDbyxklELnqxVZlhEnMe00RmtNXlT4zT1CiM2XUkitUUo1X5RAYwSNVM0hpJSNR1Tj
+GQDvHMH7H7wYxSitQIBznrquCD6g/A/etNbi6sYrVVUhpaSuKpxrjOGdJwgAj6Ixnp7f3xOcY7uX
+koiSSHq2tmIqJ5nmDgSEjSWEEO89h5CNbRrT/uAJuTm0kCBEY3sB3nmEkGjTeLDxkCd4R+0tbO5H
+GYRonqOsbV4QAhiNEDThJj3Ob8LNe4IQ6LquSSJBMb+DJEbLDqv5HEFgf2vAfSFwm9AQgBQCAc0D
+mtMhnGvCSza5JVXjPSEEMki8dwQNUor3BxZC4JxDSYXXAS003nlq6sZwm+u0VoSgYRMB3nu89yjv
+sbbJI2stuixLYi2JtCB4j/OerNXG2pp8MeNkZ4/7UlA4scmaTXw/eEgqpNKbsFIo80P4KaXe3+G9
+33jQN/GNQAiIohgjIkIIOGubCPAe4SRBBqTSGEBKiXNNoldVSV1VgMOHJo908BatBFpKUAofwPqA
+1IY4Sbi6vODw6JhZrcitINBY6n1ia4UypvGA1Jtc2RxISgQNmDjnmuOH5lCB0ACB0htLO5QySKUJ
+/gHRNogZNO1EESlQQuCQfPXqbYN4zuKcQ7MJF6M1QRkCAakU7U6XVtYijlMuLy94/PgJN+tAXoEy
+5j3SSalQ2qB0hNIKIR5gXDWhhAAhiIxBCEHw7j38eu+xwRMCTV7JxstJ6pBCYG1NL1KIakm5XiDi
+mKJyWA9pnJDna4Jv7tEASkmM1mBiRJSCVAQfEErT7Q+RAi4vLzk9OeWm1NRe8IDDSjfJ+YDryugm
+R4RAbpI2iAYctNIELwnBbzzUWDiEJokfwjGKElItSOqcYrHAC0Wr08V7qOoCrSDa1C4fAjiHbGqC
+IIkNWRqhdESSddAmoi4rAoJWp4fSEefn33O8FZHGGi3VxitgjEZrjRQCfIMkG5gjCFBKNcWMQBAC
+ROO5wMYAQqC1xhiDkpJRS7DFgipfESUt+oMRUZxS1RZb1yhtqF1A6wit9cPHgJBEJiJNIlZWEJuI
+yGjanS7tdpe6LljM5tzf3fP2u1f86NOf8tXFErEpeloJpGxCFATOe0QIiOAJtoFv5wLONcgFzWGt
+d3jvfvCcr9lSBevxFfm6Imv1iJIMtKGoAx5JCJbaB1br4n2RDSGgIxNTWY8yMVJKtjoJ66pExm28
+k2iTEJAcnD4ijiPOv39D+vVXPPvwYy6XnkBjTUkDpw+1QkqBtxYTxzjvCIALHufY5IoHIQk4rHfs
+dWKicsLlu7fErT7HHzwj7XSxtSXPS2qviPMlaE0R9HvkZINoWinF/XxJGhuEaSGATqqxviROeoQQ
+iGNDvfaYTTH7/uKSk0dPuL+eouOMdqeLtTVCgKstaZq8x3wdpyRJRgAm9xOyrIVUEhU30FyVOU92
+2qwuX3F3fU2vNyLt9DFCUeUlJmkTqIi1opUYpG8xWVmUVCA9nuY5WghBWVm8MqRJipMGIQVtbTDU
+xKJiPZ8xn9xxP5kwnU4pqpKXL1+xv3vI9WxJvbQkaUZQEYaAK0ustSzXOXHWwtY1rU6HNElZLleb
+QgrClfzouM/tyy8Zj8cMBiN2Dk+R0lB7CEGwXMyJsERRwBkJnR7VbIzRDe+rbQM6OooilFJcXFxx
+cBhhIkeBwtUOQk2IPMFbnHWUZUlVV3gfuLy+JW31SJUgVoJgK7TSIAVKR9wsl0Qm4ur8Lds7O8zr
+mrqucOucxbrEuprjYZtqUvLq5UtGox2ybg+TtUmzLs5DsVqQhhVSwMpKtDLkziCFRKkG/bRSeEB1
+Or3PlJJ0OxmDwQC3nFBbx83tFdI7itJSV5YgoaprDo+PMUazWq+pqpp22iJKE9I0Y11WGAkhOG5u
+b2lnGb1uC7xltcxZr5cc7QwZdlv0WgnVcsLk8px3Fxc8OjxEKUmcthju7KKlJNaANORlwNclJoq4
+mJcUVUUIDoJAiA2r9iFQWUdZOySO8WxJ4RaUZcVUgI5bmCxj2O3SHu7g6wrrHM55ptN7autpLZc4
+F9je2WXQHTCezEhjTb5eEBvJ6ekJ08USgsOohnvNbs4RtmBrsMUTLSmqnJ7uIao1dr0k6/YpdMTa
+KtK0xlaeiY02sKtwLsKHGuEE4NFFsabVarNcLvAIFkVBXlr29w4Y7WwTxTHWVSgpGN/cYG2FkIok
+a1FaT1Hm6EJTVo502aKqSlpJhNYSay11XXBzfc79dMn29pDx7B5XFWhfo2NDnKT84oMPmU/GCJ2Q
+z2csxzcgFT4I2tqx9BXnF+dcsEUAlIkxG1JceY+UBm1tTVmWFDZjPJujTIJdTFgs7jk6O6XdH7Cc
+jnn93bfkec5ytaSqa7r9PtoYBr0+Uilmy5wojhhu9XF1TUdq4iRBiYaEvru44uZyTRJFrFZLcCW9
+TpvgPOvlmuNnz6nLgmK+YD65Z7UsSA3IakUlYF44fGbQRqG02iBohSkjnKtRaZp99sBiy9rSymKq
+suTkyTP6gyHju1tmiyXOeW5ubwhAK2sgd3s0xDpHu92h1e5SVTVb3TbgWeRrILAzGpDnBWkSs9Vt
+s7e7w93dLVtbA2bTOVoKiqpECkEaa/qDIc7VrGdTyvUauxxj6zU+2yIn2XSfDTnVWiOlIASPare7
+nyVZhlaGTq+D95bKOgajHebzBXd3dzhXM53OqF2g3+txdnKM957T00ebqhozGu3gPThviYxmvV6j
+pWBvMCBqtYmiiEgrdGTYHY3w3lFXFVVVNdcK6G0N6Q62aPeGvLt4y+W7c/Ki4ODpc+5KSWFBbIio
+EHLDrmn6JJMkRElKlCbsbG/T7XTZPzxECFiuc4IUSKVBabZ3dzg+OsQGSZS2yIuSbn9IXVmGwx5P
+Hh1T1Y40S7m5uWY2ueObV9/y5ZdfIkTARDE+NN5vt9t89NGHZO02zjUE9Ysvv+Ty4gohAov5HCEV
+O8enTArPJK9/0BWspSwLrK0b5m4MstPpkmUtojgmzVJMknF0ckbtAsZE5HlBkIr9/X263S5eaNZF
+xf7+EU+fPuN+MmU8mbAzHNLLYqy1dNKMrW6XTq9HnMScHu3x6GiP5WKOdZ4oikizDneTGds7O7R7
+PfJ1gVaal6++ZTqbk3b7YAwrJ3l5s8CHhik4a7GuJnjXkM2HRi6K40ZiAawXDHf2sNbhbM1qtaLT
+6zEajUizlE6nizYR1lqKokAISVXkeG/xdUmxXnJ7e0O+XJKvC+I44c33b3BViatL5vMZL168Ynt3
+j9HuPt2tEVmnx8nJKTpJGe3to6RmOp1ikgQrJPOiJjIxQj60uQ42bLmRsDTKGLSta6q6ot3ukLZb
+dHpdrs/PcR463S7tTgcQZGnGzu4OSimODvaZzqbYOqfMFzw63CXSithEHIz6VFXByeE+OjKkUUwW
+KSIJ88kdKsroDwacX92ymM0xCsqypNVqcXV923AoJNu7e0ynC7q9PlcFSCdxoekilWp0MqObFpsK
+tHOOJIpx1pLECYvplFanRxtPVVbsbO8RfM1oNOLo6AgtYTmd0sliBr0OnVZCGmuKfM79dMwnHz1D
+6ojz80t6/S6nhzsIW6OVRMnAwc4A7xzfvXjBsN+j085wzrNeF3RpxIbZdMZZf8Dh8SnLdUmgBOFR
+SiLUxhNCoFQjFnqt0UI0SBBFjToopcboCGMUjx7v0e20ubg4Zzq+RVRrXr/6LWmy4os/X/Hhk0/4
++PlHDIcj7u6nvP7+nJ9+8hPSNKXX67EzHDRs4eaK1XJBEhsePz4CV/Hk9IDeYEQSRdxcXbHK16Ak
+dW0J3jOe3NHK2lzOVljnUdIgRGi4nFJoJRAhNAVSKdT27t5nAogjw2g0QiIpigJrLVGSkOc5q+k9
+j/e3+OJ3v+Vv/lPCzWSKefZjnjx9gp19QXvwjJNHH/DyxUuGo20CgeFwQDttMZ1O6PW7fPrpx+wP
+O9ze3fL5//0NxfI1l2+vyVpdnPfMZ3MGo22q2tJptdC60bEupjlq0wlKKfEERAhoJTfXNH2QfIAv
+V1u8tUgpKMuCyeSOf/vNv/DqxZ/5+U8/4Wd/8R/Z2TmmOxiSxA3XYfUVx/spn//bb1kUFqTi8uoa
+KTXdThcdR8RxzLOPfkTtIK+/ZpG9pvXjH/PX/+UJP3oy5nf/+hu0EkRJgrUehcRvZM77+YLKBYRq
+FEylFMIHQnCb6GlUmqZTFBJvLSF4rq6vWa6WpGnC3e01MlhOjo/wCFaV5We//Av+9E3Ojz95znH9
+Lfu7I5ZrwcvzObUXdPtbxGlCUa6Z3t/j6oqqsgx3D7k8/4aTk4R8PMZe/G9qVqTdbc4eP+H08WOU
+gHfnbxnfXHH+/Rtm0ynzvMR5j3WNfPSgkTVCh9wIhs3fVK/X/0wpiRABrRSj7R0m41v6vQ5PHj9i
+sViQr5ZU64LHHzxDygFvvr+hnQlurhf8+ncVl1c3fPyTT7k4/55ev4+taiIpMFGE94Hto2P+z7/+
+gZ09wVYn4Xh/i6S1x6//+2v+89/8A8YY0sjQ63RYrXLa7RadTod5aVk7gZCNpEQIKCXRSqE3B7G2
+xvmADsFD8EihKaoSISVFkXN48IgkTWi125y/u+T65o6vX7zi5z/5hJ//4u9YlTV//uYlMvwzs/mM
+u6tLkiQjzws0gdIYTBTT6kjy2ZRvX7/mxZuc58+7BCJqb/nrf/wn4jTi3ZvXVEWJc56j0xMuzs9Z
+LJa4oBGqoSONZB1QUqKVRCIJwRGC+He61kZSCc4RxRFPHp8RxxF7e3sEPLfXl1gXcM7xzavvOH93
+yUcf/5hPfvIxq7ImGR0ymy+5v5/T73fo9PtUtmKd50ghuXz3jmxrl6dnR3z46ackWcLNxQXfvXrF
+1cUllXXsDgc8+w8fkSQR88ktN+N76mxIFBncpv8RG91ZblRO7wJBuAbVet3+Z0IEyrJECMn+4RFn
+Z8csp2PiNGV7e4dBv88f/vA51zfXGK0RSuGC4upmzHg65+76mtV8yt7+AdujEaPBFrvbA9ZFRVVW
+XF9eUaA5e3TG3s6Ir776E19/+Sdurm/QStGKIp48e8bR6Qm2rpHB4XXE3Kn3Cv+DOmm0QiuJ2IjZ
+1jnqukInaUK318X7RiVZTO9ZzLpYH4iNYb1aoaKYf/jH/8YfP/+c8XiMMobHaUpeW6qywkSaODFs
+7++TKEkaa5TUdDpdhFTc3N0xnc1YLNd8/vvPeX1+zv3tDVIKep02R4f7HB4fsVrMqYqCTneLarwm
+ULyfADTyKygpwHmss43M5DzBe1S/3/vMe0ddlmgJVVXQ7/e5H485f/ManOPq6pLF7J7BcMTN7d2G
+ERumk3t6W0Nur67I87zRiyNDN0tot7usiop35++4ur7Z9Cwd2qkh2BKjFc+envH4yRO2Bn20aurG
+anbP/XTBi8tGO5Abyi5EIDbNEOlBuHbObaZeHrV/ePhZbCK0bhqUOIp+GKAEy2q15OzxI7Z3hjx9
++hQhIF/l1NY2Ek8cc3p2wu3NNUJp+r0erazFfD7j5nbMYj7F1o7jsxOkaITtar1CS8FgOKCVRiym
+93zxxy+Yjm9YLuZc3M6ZrJvZoPUO5y1aSiJj0FJhbd2M47x/fyAdRQajDc5BZAxGKxaLBUfHh1y+
+fc3Hz59zdnZGq52hpGRvf588X5NXnjgyPHp8hneOJ49OubtfcPntKxa3V9TWQoBur8tf/vJXKK1Z
+zCYs7u85ODig3+tw+uQxs8kYt7VFVRaslguKouY2t7iHISgerRWYhkI9iN1CiM1wtMkVaXREp9sh
+jhPanTZJHINzWOs4ODjg6xcv+d3vf0++LqitByHobg1ZFyWrPOf25oadnW3OTo+5f/eKePySX23V
+/LJToe6+w5YF7W6HLEtIohjrPL1ej0cffIiR4n3NODl7RJpmZN0t8spuBPJmSqaEBNcMiKT8gcI3
+478GftXJ6elnSknW+Yq6rOj1+qzzFdZ7nK3ZHvRYTO+pqpI46+AC9HoDDvZ2kcEzm004PNhn5/CY
+k8dP+dPXrxFVSStOWe8+47/+3d/T63W5fvuWm5s78vUaISVZq0UUZ6yLNbfv3vLm9UuMifnucsLa
+gVSNOK2UQmvZKIuyGaIGAt55bO3eq5bq8eMnn9m62kyTPHe3N0RRBELSbreROuLs7BGd7oDVaoXz
+8MEHTynWCyKt2R5uoZUhTlNaWYuqrPgfv/4XcpHwq7/9ew729xChJk2a4dDh/jZbo23m8zlVsWI+
+uSafT7BVxar03C1LShcQUv4wVRYCrQ1s2G7wnrq2m6ruEYAOeKRUuFDhnKPVbuGcY2d/n939fYSz
+TOcL3nz7DfvHZ5w83uZufM9gMMTVNdY6TJKwWMwp8jX9wRbBRIyefYi3FdZVYD1vX7/BRDFJp0Or
+04Zqyer+gmpd4p2nN9xneTenqCZIodHGUJZlM3uUBu9BCI9sxi5oJfFaI72nrmt0CAJJM7fwQJRm
+nD56RL5cYmtLGhmyWNLfek6VL1iMr5lNbhlujzBa0e1uka/XBO+ZTWfc3tyg45i6toxvr8niBjK7
+w22wBUorFtNbynyBy+e0sgyRDKhEyvLdmEgrPArBhpV7h4p0M9YDlHgYk0ucD+ACwjnUzu7OZ943
+o6v21hbBB6hrBtu7fP2nP5K1OxweHWNEYDgaUeVLWt0es8mEqizJ12u8h/HknqqquHz7lt3hgKIq
+sSE0CocQ4GuSLGG9mGPLHOMK2qMDkt4eXqcsCsd3r99Q1jUog6dpgR+2JYw2TY5s9i6stbjNFNpa
+i272TmC0vcvt1SXaaOh2eP3i//Hs+SeMhkMWyzXbwz62XLF3csZqsaDXbqG0Il8tGK8WoGOG2/v8
+6q/+CrynnWUgAnVdcXf9PVmkWY0d2/tHFIslRC2K2pFkEeuipKwdelO1MXKjmDSVW0uJ180YPNCI
+ELXzVPUPhVEmSUwcxVycv6VYr6mrmqoseP7JT7Flwbu355R1zbffvUHomLIsieMILQWtWHOwM+J4
+u88gctjVlPl8jtQKExsIjvViiqtKymJN1t3CASbbwqkEGyR5UZF1+iAVi/EYpTVJ2oJN57dpmpol
+hc0GxkPteJjdCwK6KBqpppVlrJYrnK3ZPzri/O05CDg6Pubi7ffs7x9QW0d+e0cnizGy2R+x64Jg
+SzpZRtbrsrawXkwpVjNsXSG8RzbbL1TFGiEVrd4QW1ekWZegYmrrWS3mrNc5cdqC4DcTY0WSpiht
+QP6wnPPweb+AAOj5bEaaxqxWK+LY0N3q8+LrF0Ra8+jJI6aTCe1WxjpfkqZHpFpT5Qv6wz5VndNO
+U0wWEyUt4labWDQvtc7jfcDXFVKAwqG0bnRbExGlbWSUEIKgrNbcXl7Q6XbJPWBrImMgTZteXan3
+7e/Dz4PqGAL4EJp+xDmPNhqtIxbTBd5Zkm6LgODq4pzR7gGtVsY//6//yc9+8ilGSWrv6PSGSKVI
+kpg0zVBJRqpjXAhUdY0gEKxGbQQ1rTVJ1kLHMVIn1M7hPdzfj6mLHCEF1joiqXDOvl9KeL/+sYFe
+ABFoFnLqBr61c5bgJQhDZWvy5ZI0idneO+D1qxfsHx5z/uY7fvqLX9BODJfv3rG3PYTQwkQpcWQQ
+WmPiDGUSlNZY7xuq4z1qM3qWWiHY/JYaKSTW1qzzkuVsSl0WdLp98Ip8k8RShPdKifeBsBEa2Czd
+BNcsrHnv+f9Qc51Rfhz5VAAAAABJRU5ErkJggg=="/>
+</svg>
diff --git a/test/svg/doc.3.image-transform.svg b/test/svg/doc.3.image-transform.svg
new file mode 100644
index 000000000..90e5ecb1a
--- /dev/null
+++ b/test/svg/doc.3.image-transform.svg
@@ -0,0 +1,122 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <image x="0" y="-1000" width="1000" height="1000"
+         transform="scale(0.5) rotate(-45),translate(800,500)"
+         xlink:href="data:image/png;base64,
+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
+BNcsrHnv+f9Qc51Rfhz5VAAAAABJRU5ErkJggg=="/>
+</svg>
diff --git a/test/svg/doc.4.clip-user.svg b/test/svg/doc.4.clip-user.svg
new file mode 100644
index 000000000..26cdd818a
--- /dev/null
+++ b/test/svg/doc.4.clip-user.svg
@@ -0,0 +1,9 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="userSpaceOnUse" >
+    <circle  cx="500" cy="-500" r="400" />
+  </clipPath>
+
+  <rect x="100" y="-900" width="800" height="800"
+        stroke="green" stroke-width="100"
+        clip-path="url(#clip)" />
+</svg>
diff --git a/test/svg/doc.5.clip-object.svg b/test/svg/doc.5.clip-object.svg
new file mode 100644
index 000000000..732911590
--- /dev/null
+++ b/test/svg/doc.5.clip-object.svg
@@ -0,0 +1,10 @@
+<svg  xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="objectBoundingBox">
+    <circle cx=".5" cy=".5" r=".5" />
+  </clipPath>
+
+  <rect x="100" y="-900" width="800" height="800"
+        stroke="green" stroke-width="100"
+        clip-path="url(#clip)" />
+
+</svg>
diff --git a/test/svg/doc.6.clip-user2.svg b/test/svg/doc.6.clip-user2.svg
new file mode 100644
index 000000000..a68c1c42f
--- /dev/null
+++ b/test/svg/doc.6.clip-user2.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="userSpaceOnUse" >
+    <circle  cx="500" cy="-500" r="400" />
+    <rect x="100" y="-900" width="800" height="400"/>
+  </clipPath>
+
+  <rect x="100" y="-900" width="800" height="800"
+        stroke="green" stroke-width="100"
+        clip-path="url(#clip)" />
+</svg>
diff --git a/test/svg/doc.7.clip-object2.svg b/test/svg/doc.7.clip-object2.svg
new file mode 100644
index 000000000..f5c153dd1
--- /dev/null
+++ b/test/svg/doc.7.clip-object2.svg
@@ -0,0 +1,11 @@
+<svg  xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="objectBoundingBox">
+    <circle cx=".5" cy=".5" r=".5" />
+    <rect x="0" y="0" width="1" height="0.5"/>
+  </clipPath>
+
+  <rect x="100" y="-900" width="800" height="800"
+        stroke="green" stroke-width="100"
+        clip-path="url(#clip)" />
+
+</svg>
diff --git a/test/svg/doc.8.clip-user3.svg b/test/svg/doc.8.clip-user3.svg
new file mode 100644
index 000000000..8ff61d593
--- /dev/null
+++ b/test/svg/doc.8.clip-user3.svg
@@ -0,0 +1,15 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="userSpaceOnUse" >
+    <circle  cx="500" cy="-500" r="400" />
+  </clipPath>
+
+  <clipPath id="clip2" clipPathUnits="userSpaceOnUse" >
+    <rect x="100" y="-900" width="800" height="400"/>
+  </clipPath>
+
+  <g clip-path="url(#clip)">
+     <rect x="100" y="-900" width="800" height="800"
+           stroke="green" stroke-width="100"
+           clip-path="url(#clip2)" />
+   </g>
+</svg>
diff --git a/test/svg/doc.9.clip-object3.svg b/test/svg/doc.9.clip-object3.svg
new file mode 100644
index 000000000..b291df79f
--- /dev/null
+++ b/test/svg/doc.9.clip-object3.svg
@@ -0,0 +1,15 @@
+<svg  xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
+  <clipPath id="clip" clipPathUnits="objectBoundingBox">
+    <circle cx=".5" cy=".5" r=".5" />
+  </clipPath>
+
+  <clipPath id="clip2" clipPathUnits="objectBoundingBox">
+    <rect x="0" y="0" width="1" height="0.5"/>
+  </clipPath>
+
+    <g clip-path="url(#clip)">
+      <rect x="100" y="-900" width="800" height="800"
+            stroke="green" stroke-width="100"
+            clip-path="url(#clip2)" />
+    </g>
+</svg>
diff --git a/test/svg/doc.A.g.svg b/test/svg/doc.A.g.svg
new file mode 100644
index 000000000..bdaade725
--- /dev/null
+++ b/test/svg/doc.A.g.svg
@@ -0,0 +1,14 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink" >
+  <defs>
+    <circle id="circle"
+            cx="0" cy="0" r="250" />
+  </defs>
+
+  <g opacity="0.5">
+    <use xlink:href="#circle" x="330" y="-500"
+         fill="red"  />
+    <use xlink:href="#circle" x="670" y="-500"
+         fill="green"  />
+  </g>
+</svg>
diff --git a/test/svg/fill.0.name.svg b/test/svg/fill.0.name.svg
new file mode 100644
index 000000000..e377b8827
--- /dev/null
+++ b/test/svg/fill.0.name.svg
@@ -0,0 +1,4 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="indigo" />
+</svg>
diff --git a/test/svg/fill.1.hex6.svg b/test/svg/fill.1.hex6.svg
new file mode 100644
index 000000000..04759d78a
--- /dev/null
+++ b/test/svg/fill.1.hex6.svg
@@ -0,0 +1,4 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="#AA55AA" />
+</svg>
diff --git a/test/svg/fill.2.hex3.svg b/test/svg/fill.2.hex3.svg
new file mode 100644
index 000000000..9f9bce580
--- /dev/null
+++ b/test/svg/fill.2.hex3.svg
@@ -0,0 +1,4 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="#A5A" />
+</svg>
diff --git a/test/svg/fill.3.rgb.svg b/test/svg/fill.3.rgb.svg
new file mode 100644
index 000000000..c18b03bb5
--- /dev/null
+++ b/test/svg/fill.3.rgb.svg
@@ -0,0 +1,4 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="rgb (75, 0 , 130)" />
+</svg>
diff --git a/test/svg/fill.4.current-color.svg b/test/svg/fill.4.current-color.svg
new file mode 100644
index 000000000..cf77d65a6
--- /dev/null
+++ b/test/svg/fill.4.current-color.svg
@@ -0,0 +1,4 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="currentColor" />
+</svg>
diff --git a/test/svg/fill.5.palette.svg b/test/svg/fill.5.palette.svg
new file mode 100644
index 000000000..53ea97512
--- /dev/null
+++ b/test/svg/fill.5.palette.svg
@@ -0,0 +1,4 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="var(--color1, red)" />
+</svg>
diff --git a/test/svg/fill.6.opacity.svg b/test/svg/fill.6.opacity.svg
new file mode 100644
index 000000000..59b4eb41f
--- /dev/null
+++ b/test/svg/fill.6.opacity.svg
@@ -0,0 +1,4 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"
+        fill="magenta" fill-opacity="0.5" />
+</svg>
diff --git a/test/svg/fill.7.color.svg b/test/svg/fill.7.color.svg
new file mode 100644
index 000000000..745b2e881
--- /dev/null
+++ b/test/svg/fill.7.color.svg
@@ -0,0 +1,5 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     color="green" >
+  <rect x="100" y="-750" width="800" height="500"
+        fill="currentColor" />
+</svg>
diff --git a/test/svg/fill.8.rule.svg b/test/svg/fill.8.rule.svg
new file mode 100644
index 000000000..366ff6760
--- /dev/null
+++ b/test/svg/fill.8.rule.svg
@@ -0,0 +1,8 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <g transform="scale(5,5) translate(0,-150)">
+    <polygon fill-rule="nonzero"
+             points="50,0 21,90 98,35 2,35 79,90"/>
+    <polygon fill-rule="evenodd"
+             points="150,0 121,90 198,35 102,35 179,90"/>
+  </g>
+</svg>
diff --git a/test/svg/gradient.0.lin-pad.svg b/test/svg/gradient.0.lin-pad.svg
new file mode 100644
index 000000000..0ec54092f
--- /dev/null
+++ b/test/svg/gradient.0.lin-pad.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="grad" x1="33%" x21="66%">
+      <stop offset="0%" stop-color="blue" stop-opacity="1" />
+      <stop offset="100%" stop-color="red" stop-opacity="0.5" />
+    </linearGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>
+
diff --git a/test/svg/gradient.1.lin-reflect.svg b/test/svg/gradient.1.lin-reflect.svg
new file mode 100644
index 000000000..9e3e50cb9
--- /dev/null
+++ b/test/svg/gradient.1.lin-reflect.svg
@@ -0,0 +1,11 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="grad" x1="33%" x2="66%"
+                    spreadMethod="reflect">
+      <stop offset="0%" stop-color="blue" stop-opacity="1" />
+      <stop offset="100%" stop-color="red" stop-opacity="1" />
+    </linearGradient>
+  </defs>
+  <rect x="0" y="-900" width="1000" height="800" fill="url(#grad)" />
+</svg>
+
diff --git a/test/svg/gradient.2.lin-repeat.svg b/test/svg/gradient.2.lin-repeat.svg
new file mode 100644
index 000000000..1f023f5e1
--- /dev/null
+++ b/test/svg/gradient.2.lin-repeat.svg
@@ -0,0 +1,11 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="grad" x1="33%" x2="66%"
+                    spreadMethod="repeat">
+      <stop offset="0%" stop-color="blue" stop-opacity="1" />
+      <stop offset="100%" stop-color="red" stop-opacity="1" />
+    </linearGradient>
+  </defs>
+  <rect x="0" y="-900" width="1000" height="800" fill="url(#grad)" />
+</svg>
+
diff --git a/test/svg/gradient.3.lin-user.svg b/test/svg/gradient.3.lin-user.svg
new file mode 100644
index 000000000..fc90328f0
--- /dev/null
+++ b/test/svg/gradient.3.lin-user.svg
@@ -0,0 +1,11 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="grad" x1="300" y1="-500" x2="600" y2="-500"
+                    gradientUnits="userSpaceOnUse">
+      <stop offset="0%" stop-color="blue" stop-opacity="1" />
+      <stop offset="100%" stop-color="red" stop-opacity="1" />
+    </linearGradient>
+  </defs>
+  <rect x="0" y="-900" width="1000" height="800" fill="url(#grad)" />
+</svg>
+
diff --git a/test/svg/gradient.4.lin-transform.svg b/test/svg/gradient.4.lin-transform.svg
new file mode 100644
index 000000000..bc8f5b622
--- /dev/null
+++ b/test/svg/gradient.4.lin-transform.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="grad" gradientTransform="rotate(45)">
+      <stop offset="0%" stop-color="blue" stop-opacity="1" />
+      <stop offset="100%" stop-color="red" stop-opacity="1" />
+    </linearGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>
+
diff --git a/test/svg/gradient.5.rad-pad.svg b/test/svg/gradient.5.rad-pad.svg
new file mode 100644
index 000000000..b631ddc73
--- /dev/null
+++ b/test/svg/gradient.5.rad-pad.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <radialGradient id="grad" cx="50%" cy="50%"
+                    fx="0.75" fy="0.35" r="0.5">
+      <stop offset="0%" stop-color="white" stop-opacity="1" />
+      <stop offset="100%" stop-color="green" stop-opacity="1" />
+    </radialGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>
diff --git a/test/svg/gradient.6.rad-reflect.svg b/test/svg/gradient.6.rad-reflect.svg
new file mode 100644
index 000000000..b6fea76be
--- /dev/null
+++ b/test/svg/gradient.6.rad-reflect.svg
@@ -0,0 +1,11 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <radialGradient id="grad" cx="75%" cy="25%" r="33%"
+                    fx="0.64" fy="0.18" fr="0.17"
+                    spreadMethod="reflect" >
+      <stop offset="0%" stop-color="white" stop-opacity="1" />
+      <stop offset="100%" stop-color="green" stop-opacity="1" />
+    </radialGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>
diff --git a/test/svg/gradient.7.rad-repeat.svg b/test/svg/gradient.7.rad-repeat.svg
new file mode 100644
index 000000000..63d138d2a
--- /dev/null
+++ b/test/svg/gradient.7.rad-repeat.svg
@@ -0,0 +1,11 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <radialGradient id="grad" cx="75%" cy="25%" r="33%"
+                    fx="0.64" fy="0.18" fr="0.17"
+                    spreadMethod="repeat" >
+      <stop offset="0%" stop-color="white" stop-opacity="1" />
+      <stop offset="100%" stop-color="green" stop-opacity="1" />
+    </radialGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>
diff --git a/test/svg/gradient.8.rad-user.svg b/test/svg/gradient.8.rad-user.svg
new file mode 100644
index 000000000..e1d9c61f0
--- /dev/null
+++ b/test/svg/gradient.8.rad-user.svg
@@ -0,0 +1,11 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <radialGradient id="grad" cx="450" cy="-550" r="400"
+                    fx="600" fy="-400" fr="10"
+                    gradientUnits="userSpaceOnUse" >
+      <stop offset="0%" stop-color="red" stop-opacity="1" />
+      <stop offset="100%" stop-color="green" stop-opacity="0.5" />
+    </radialGradient>
+  </defs>
+  <rect x="0" y="-1000" width="1000" height="1000" fill="url(#grad)" />
+</svg>
diff --git a/test/svg/gradient.9.rad-transform.svg b/test/svg/gradient.9.rad-transform.svg
new file mode 100644
index 000000000..4b90aaaf6
--- /dev/null
+++ b/test/svg/gradient.9.rad-transform.svg
@@ -0,0 +1,11 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <radialGradient id="grad" cx="50%" cy="25%"
+                    fx="0.75" fy="0.35" r="0.5"
+                     gradientTransform="scale(1, 2)">
+      <stop offset="0%" stop-color="red" stop-opacity="1" />
+      <stop offset="100%" stop-color="green" stop-opacity="1" />
+    </radialGradient>
+  </defs>
+  <rect x="100" y="-900" width="800" height="800" fill="url(#grad)" />
+</svg>
diff --git a/test/svg/meson.build b/test/svg/meson.build
new file mode 100644
index 000000000..b2b017d79
--- /dev/null
+++ b/test/svg/meson.build
@@ -0,0 +1,5 @@
+if librsvg_dep.found()
+  executable('svg-render',
+             'svg-render.c',
+             dependencies: [libcairo_dep, librsvg_dep])
+endif
diff --git a/test/svg/path.0.line.svg b/test/svg/path.0.line.svg
new file mode 100644
index 000000000..19d50914a
--- /dev/null
+++ b/test/svg/path.0.line.svg
@@ -0,0 +1,8 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <path fill="none" stroke="black" stroke-width="20"
+        d="M 200 -200 
+           L 500 -500
+           H 800
+           V -200
+  " />
+</svg>
diff --git a/test/svg/path.1.curve.svg b/test/svg/path.1.curve.svg
new file mode 100644
index 000000000..49c252ac8
--- /dev/null
+++ b/test/svg/path.1.curve.svg
@@ -0,0 +1,9 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <path fill="none" stroke="black" stroke-width="20"
+        d="M200,-400
+           C200,-200
+           500,-200
+           500,-400
+           S800,-600
+           800,-400" />
+</svg>
diff --git a/test/svg/path.2.quad.svg b/test/svg/path.2.quad.svg
new file mode 100644
index 000000000..00a0dde57
--- /dev/null
+++ b/test/svg/path.2.quad.svg
@@ -0,0 +1,8 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <path fill="none" stroke="black" stroke-width="20"
+        d="M200,-300
+           Q400,-50
+           600,-300
+           T1000,-300"
+        />
+</svg>
diff --git a/test/svg/path.3.arc.svg b/test/svg/path.3.arc.svg
new file mode 100644
index 000000000..f177a72af
--- /dev/null
+++ b/test/svg/path.3.arc.svg
@@ -0,0 +1,7 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <path fill="none" stroke="black" stroke-width="20"
+        d="M500,-500
+           L 712 -712
+           A300,300 0 1,0 712, -288 z"
+        />
+</svg>
diff --git a/test/svg/shapes.0.rect.svg b/test/svg/shapes.0.rect.svg
new file mode 100644
index 000000000..17db34649
--- /dev/null
+++ b/test/svg/shapes.0.rect.svg
@@ -0,0 +1,3 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500"/>
+</svg>
diff --git a/test/svg/shapes.1.rounded-rect.svg b/test/svg/shapes.1.rounded-rect.svg
new file mode 100644
index 000000000..717470bee
--- /dev/null
+++ b/test/svg/shapes.1.rounded-rect.svg
@@ -0,0 +1,3 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <rect x="100" y="-750" width="800" height="500" rx="100" ry="100"/>
+</svg>
diff --git a/test/svg/shapes.2.circle.svg b/test/svg/shapes.2.circle.svg
new file mode 100644
index 000000000..3c07bad83
--- /dev/null
+++ b/test/svg/shapes.2.circle.svg
@@ -0,0 +1,3 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <circle cx="500" cy="-500" r="400"/>
+</svg>
diff --git a/test/svg/shapes.3.ellipse.svg b/test/svg/shapes.3.ellipse.svg
new file mode 100644
index 000000000..67260a404
--- /dev/null
+++ b/test/svg/shapes.3.ellipse.svg
@@ -0,0 +1,3 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <ellipse cx="500" cy="-500" rx="400" ry="200"/>
+</svg>
diff --git a/test/svg/shapes.4.line.svg b/test/svg/shapes.4.line.svg
new file mode 100644
index 000000000..58de42947
--- /dev/null
+++ b/test/svg/shapes.4.line.svg
@@ -0,0 +1,3 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="200" y1="-200" x2="800" y2="-800" stroke="black" stroke-width="20"/>
+</svg>
diff --git a/test/svg/shapes.5.polyline.svg b/test/svg/shapes.5.polyline.svg
new file mode 100644
index 000000000..0cc5c383c
--- /dev/null
+++ b/test/svg/shapes.5.polyline.svg
@@ -0,0 +1,12 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <polyline fill="none" stroke="black" stroke-width="20"
+            points="100, -100,
+                    300, -100,
+                    300, -300,
+                    500, -300,
+                    500, -500,
+                    700, -500
+                    700, -700
+                    900, -700
+                    900, -900" />
+</svg>
diff --git a/test/svg/shapes.6.polygon.svg b/test/svg/shapes.6.polygon.svg
new file mode 100644
index 000000000..56bda0705
--- /dev/null
+++ b/test/svg/shapes.6.polygon.svg
@@ -0,0 +1,12 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<polygon points="350, -75
+                 379, -161
+                 469, -161
+                 397, -215
+                 423, -301
+                 350, -250
+                 277, -301
+                 303, -215
+                 231, -161
+                 321, -161" />
+</svg>
diff --git a/test/svg/stroke.0.name.svg b/test/svg/stroke.0.name.svg
new file mode 100644
index 000000000..f2a926475
--- /dev/null
+++ b/test/svg/stroke.0.name.svg
@@ -0,0 +1,6 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="indigo" />
+</svg>
diff --git a/test/svg/stroke.1.hex6.svg b/test/svg/stroke.1.hex6.svg
new file mode 100644
index 000000000..89f2b1b38
--- /dev/null
+++ b/test/svg/stroke.1.hex6.svg
@@ -0,0 +1,6 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="#AA55AA" />
+</svg>
diff --git a/test/svg/stroke.2.hex3.svg b/test/svg/stroke.2.hex3.svg
new file mode 100644
index 000000000..a8565a7b6
--- /dev/null
+++ b/test/svg/stroke.2.hex3.svg
@@ -0,0 +1,6 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="#A5A" />
+</svg>
diff --git a/test/svg/stroke.3.rgb.svg b/test/svg/stroke.3.rgb.svg
new file mode 100644
index 000000000..42e7514e4
--- /dev/null
+++ b/test/svg/stroke.3.rgb.svg
@@ -0,0 +1,6 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="rgb(75,0,130)" />
+</svg>
diff --git a/test/svg/stroke.4.current-color.svg b/test/svg/stroke.4.current-color.svg
new file mode 100644
index 000000000..9d645b881
--- /dev/null
+++ b/test/svg/stroke.4.current-color.svg
@@ -0,0 +1,6 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="currentColor" />
+</svg>
diff --git a/test/svg/stroke.5.palette.svg b/test/svg/stroke.5.palette.svg
new file mode 100644
index 000000000..1262a7ba0
--- /dev/null
+++ b/test/svg/stroke.5.palette.svg
@@ -0,0 +1,6 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="var(--color1, red)" />
+</svg>
diff --git a/test/svg/stroke.6.opacity.svg b/test/svg/stroke.6.opacity.svg
new file mode 100644
index 000000000..b84d1bdc9
--- /dev/null
+++ b/test/svg/stroke.6.opacity.svg
@@ -0,0 +1,6 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="magenta" stroke-opacity="0.5" />
+</svg>
diff --git a/test/svg/stroke.7.color.svg b/test/svg/stroke.7.color.svg
new file mode 100644
index 000000000..952f48261
--- /dev/null
+++ b/test/svg/stroke.7.color.svg
@@ -0,0 +1,7 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     color="green" >
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="100"
+        stroke="currentColor" />
+</svg>
diff --git a/test/svg/stroke.8.width.svg b/test/svg/stroke.8.width.svg
new file mode 100644
index 000000000..bb9edea90
--- /dev/null
+++ b/test/svg/stroke.8.width.svg
@@ -0,0 +1,14 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-900" x2="900" y2="-900"
+        fill="none"
+        stroke-width="100"
+        stroke="black" />
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="200"
+        stroke="black" />
+  <line x1="100" y1="-100" x2="900" y2="-100"
+        fill="none"
+        stroke-width="300"
+        stroke="black" />
+</svg>
diff --git a/test/svg/stroke.9.cap.svg b/test/svg/stroke.9.cap.svg
new file mode 100644
index 000000000..35046446c
--- /dev/null
+++ b/test/svg/stroke.9.cap.svg
@@ -0,0 +1,17 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-900" x2="900" y2="-900"
+        fill="none"
+        stroke-width="200"
+        stroke-linecap="butt"
+        stroke="black" />
+  <line x1="100" y1="-500" x2="900" y2="-500"
+        fill="none"
+        stroke-width="200"
+        stroke-linecap="round"
+        stroke="black" />
+  <line x1="100" y1="-100" x2="900" y2="-100"
+        fill="none"
+        stroke-width="200"
+        stroke-linecap="square"
+        stroke="black" />
+</svg>
diff --git a/test/svg/stroke.A.dash.svg b/test/svg/stroke.A.dash.svg
new file mode 100644
index 000000000..2be5a1d90
--- /dev/null
+++ b/test/svg/stroke.A.dash.svg
@@ -0,0 +1,27 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-950" x2="900" y2="-950"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="none"
+        stroke="black" />
+  <line x1="100" y1="-750" x2="900" y2="-750"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200"
+        stroke="black" />
+  <line x1="100" y1="-550" x2="900" y2="-550"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50"
+        stroke="black" />
+  <line x1="100" y1="-350" x2="900" y2="-350"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50 100"
+        stroke="black" />
+  <line x1="100" y1="-150" x2="900" y2="-150"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50 100 150"
+        stroke="black" />
+</svg>
diff --git a/test/svg/stroke.B.dash-offset.svg b/test/svg/stroke.B.dash-offset.svg
new file mode 100644
index 000000000..f7623ea84
--- /dev/null
+++ b/test/svg/stroke.B.dash-offset.svg
@@ -0,0 +1,31 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <line x1="100" y1="-950" x2="900" y2="-950"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="none"
+        stroke="black" />
+  <line x1="100" y1="-750" x2="900" y2="-750"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200"
+        stroke-dashoffset="100"
+        stroke="black" />
+  <line x1="100" y1="-550" x2="900" y2="-550"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50"
+        stroke-dashoffset="100"
+        stroke="black" />
+  <line x1="100" y1="-350" x2="900" y2="-350"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50 100"
+        stroke-dashoffset="100"
+        stroke="black" />
+  <line x1="100" y1="-150" x2="900" y2="-150"
+        fill="none"
+        stroke-width="100"
+        stroke-dasharray="200 50 100 150"
+        stroke-dashoffset="100"
+        stroke="black" />
+</svg>
diff --git a/test/svg/stroke.C.miter.svg b/test/svg/stroke.C.miter.svg
new file mode 100644
index 000000000..1dc4ee6a7
--- /dev/null
+++ b/test/svg/stroke.C.miter.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <polyline
+      points="100, -200
+              900, -400
+              100, -700"
+      fill="none"
+      stroke-width="200"
+      stroke-linejoin="miter"
+      stroke="black" />
+</svg>
diff --git a/test/svg/stroke.D.round.svg b/test/svg/stroke.D.round.svg
new file mode 100644
index 000000000..3e4c6236f
--- /dev/null
+++ b/test/svg/stroke.D.round.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <polyline
+      points="100, -200
+              900, -400
+              100, -700"
+      fill="none"
+      stroke-width="200"
+      stroke-linejoin="round"
+      stroke="black" />
+</svg>
diff --git a/test/svg/stroke.E.bevel.svg b/test/svg/stroke.E.bevel.svg
new file mode 100644
index 000000000..25d9e3803
--- /dev/null
+++ b/test/svg/stroke.E.bevel.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <polyline
+      points="100, -200
+              900, -400
+              100, -700"
+      fill="none"
+      stroke-width="200"
+      stroke-linejoin="bevel"
+      stroke="black" />
+</svg>
diff --git a/test/svg/stroke.F.miter-limit.svg b/test/svg/stroke.F.miter-limit.svg
new file mode 100644
index 000000000..14357f0b4
--- /dev/null
+++ b/test/svg/stroke.F.miter-limit.svg
@@ -0,0 +1,11 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <polyline
+      points="100, -200
+              900, -400
+              100, -700"
+      fill="none"
+      stroke-width="200"
+      stroke-linejoin="miter"
+      stroke-miterlimit="2"
+      stroke="black" />
+</svg>
diff --git a/test/svg/svg-font-template.ttx b/test/svg/svg-font-template.ttx
new file mode 100644
index 000000000..715074dda
--- /dev/null
+++ b/test/svg/svg-font-template.ttx
@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x3e7355ef"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000001"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 15 00:00:00 2022"/>
+    <modified value="Wed Jun 15 00:00:00 2022"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="1000"/>
+    <yMax value="1000"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1100"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="1000"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="2"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="2"/>
+    <maxPoints value="1"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="1"/>
+    <maxCompositeContours value="1"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="256"/>
+    <maxSizeOfInstructions value="1"/>
+    <maxComponentElements value="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="1000"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000001"/>
+    <ySubscriptXSize value="600"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="600"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="300"/>
+    <yStrikeoutSize value="0"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="djr "/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="65"/>
+    <sTypoAscender value="1000"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="720"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1100" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Cairo Font Template
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Cairo Font Template Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="2"/>
+    <palette index="0">
+      <color index="0" value="#C5A1D7FF"/>
+      <color index="1" value="#80DFC8FF"/>
+    </palette>
+    <palette index="1">
+      <color index="0" value="#6392A9FF"/>
+      <color index="1" value="#7896B3FF"/>
+    </palette>
+  </CPAL>
+  
+  <SVG>
+    <svgDoc endGlyphID="0" startGlyphID="0">
+      <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"></svg>]]>
+    </svgDoc>
+    <colorPalettes>
+    </colorPalettes>
+  </SVG>
+
+</ttFont>
diff --git a/test/svg/svg-render.c b/test/svg/svg-render.c
new file mode 100644
index 000000000..8f67ebdc2
--- /dev/null
+++ b/test/svg/svg-render.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright © 2016 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+/* Compilation:
+ *   Build cairo with -DDEBUG_SVG_RENDER
+ *   gcc -o svg-render svg-render.c `pkg-config --cflags --libs cairo librsvg`
+ */
+
+/* svg-render - render SVG files using both the cairo glyph renderer and librsvg.
+ *
+ * This allows testing the cairo SVG test cases before assembling them into an SVG font.
+ * Usage:
+ *   svg-render [-b] [-s] [-g <id>] [-e <em_size> ] input.svg
+ *
+ * Output is written to input.cairo.png and input.rsvg.png.
+ *
+ *  -b   print bounding box.
+ *  -s   Use standard SVG viewport. See below.
+ *  -g   render glyph with id <id>
+ *  -e   set the EM size. The default is 1000.
+ *
+ * SVG Glyphs are assumed to be wholely within the view port. 
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <cairo.h>
+#include <librsvg/rsvg.h>
+
+typedef enum { CAIRO_SVG, LIBRSVG } svg_renderer_t;
+
+/* output image size */
+#define WIDTH 1000
+#define HEIGHT 1000
+
+static cairo_bool_t bbox = FALSE;
+static cairo_bool_t standard_svg = FALSE;
+static const char *glyph_id = NULL;
+static int em_size = 1000;
+
+cairo_bool_t
+_cairo_debug_svg_render (cairo_t       *cr,
+                         const char    *svg_document,
+                         const char    *element,
+                         double         units_per_em,
+                         int            debug_level);
+
+static void
+cairo_render (const char *svg_document, cairo_t *cr)
+{
+    if (!_cairo_debug_svg_render (cr, svg_document, glyph_id, em_size, 2))
+        printf("_cairo_debug_svg_render() failed\n");
+}
+
+static void
+librsvg_render (const char *svg_document, cairo_t *cr)
+{
+    gboolean has_width;
+    gboolean has_height;
+    gboolean has_viewbox;
+    RsvgLength svg_width;
+    RsvgLength svg_height;
+    RsvgRectangle svg_viewbox;
+    RsvgRectangle viewport;
+    double width, height;
+    GError *error = NULL;
+
+    RsvgHandle *handle = rsvg_handle_new_from_data ((guint8 *)svg_document,
+                                                    strlen(svg_document),
+                                                    &error);
+    if (!handle) {
+        printf ("Could not load: %s", error->message);
+        return;
+    }
+
+    /* Default width if not specified is EM Square */
+    width  = em_size;
+    height = em_size;
+    
+    /* Get width/height if specified. */
+    rsvg_handle_get_intrinsic_dimensions(handle,
+                                         &has_width,
+                                         &svg_width,
+                                         &has_height,
+                                         &svg_height,
+                                         &has_viewbox,
+                                         &svg_viewbox);
+    if (has_width)
+        width  = svg_width.length;
+
+    if (has_height)
+        height = svg_height.length;
+
+    /* We set the viewport for the call rsvg_handle_render_layer() to
+     * width/height. That way if either dimension is not specified in
+     * the SVG it will be inherited from the viewport we provide.
+     *
+     * As this scales up the rendered dimensions by width/height we
+     * need to undo this scaling to get a unit square scale that
+     * matches the cairo SVG renderer scale. The OpenType SVG spec
+     * does not say what to do if width != height. In this case we
+     * will just use a uniform scale that ensures the viewport fits in
+     * the unit square and also center it.
+     */
+
+    if (width > height) {
+        cairo_scale (cr, 1.0/width, 1.0/width);
+        cairo_translate (cr, 0, (width - height)/2.0);
+    } else {
+        cairo_scale (cr, 1.0/height, 1.0/height);
+        cairo_translate (cr, (height - width)/2.0, 0);
+    }
+
+    viewport.x = 0;
+    viewport.y = 0;
+    viewport.width = width;
+    viewport.height = height;
+    if (!rsvg_handle_render_layer (handle, cr, glyph_id, &viewport, &error)) {
+        printf ("librsvg render failed: %s", error->message);
+        return;
+    }
+}
+
+static void
+render_svg (const char *svg_document, svg_renderer_t renderer, const char* out_file)
+{
+    double x, y, w, h;
+
+    cairo_surface_t *recording = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+    cairo_t *cr = cairo_create (recording);
+
+    /* Scale up to image size when recording to reduce rounding errors
+     * in cairo_recording_surface_ink_extents()
+     */
+    cairo_scale (cr, WIDTH, HEIGHT);
+
+    if (renderer == CAIRO_SVG) {
+        cairo_render (svg_document, cr);
+    } else {
+        librsvg_render (svg_document, cr);
+    }
+    cairo_destroy (cr);
+
+    if (bbox) {
+        cairo_recording_surface_ink_extents (recording, &x, &y, &w, &h);
+        if (renderer == CAIRO_SVG)
+            printf("cairo  ");
+        else
+            printf("librsvg");
+
+        printf(" bbox left: %d  right: %d  top: %d  bottom: %d\n",
+               (int)floor(x),
+               (int)ceil(x + w),
+               (int)floor(y),
+               (int)ceil(y + h));
+    }
+
+    cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT);
+    cr = cairo_create (surface);
+
+    /* If rendering a glyph need to translate base line to bottom of image */
+    if (standard_svg)
+        cairo_set_source_surface (cr, recording, 0, 0);
+    else
+        cairo_set_source_surface (cr, recording, 0, HEIGHT);
+
+    cairo_paint (cr);
+    
+    cairo_surface_write_to_png (surface, out_file);
+    cairo_surface_destroy (surface);
+}
+
+static char *
+create_output_name (const char *svg_file, svg_renderer_t renderer)
+{
+    char buf[1000];
+    int len;
+
+    strcpy (buf, svg_file);
+    len = strlen (buf);
+    
+    if (strlen (buf) > 5 && strcmp (buf + len - 4, ".svg") == 0)
+        buf[len - 4] = 0;
+
+    if (renderer == CAIRO_SVG)
+        strcat (buf, ".cairo.png");
+    else
+        strcat (buf, ".rsvg.png");
+
+    return strdup (buf);
+}
+
+static char *
+read_file(const char *filename)
+{
+    FILE *fp;
+    int len;
+    char *data;
+
+    fp = fopen (filename, "r");
+    if (fp == NULL)
+        return NULL;
+
+    fseek (fp, 0, SEEK_END);
+    len = ftell(fp);
+    rewind (fp);
+    data = malloc (len + 1);
+    if (fread(data, len, 1, fp) != 1)
+        return NULL;
+    data[len] = 0;
+    fclose(fp);
+    return data;
+}
+
+static void
+usage_and_exit()
+{
+    printf ("svg-render [-b] [-s] [-g <id>] [-e <em_size> ] input.svg\n");
+    exit (1);
+}
+
+int
+main(int argc, char **argv)
+{
+    const char *input_file = NULL;
+    char *svg_document;
+    char *output_file;
+
+    argc--;
+    argv++;
+    while (argc > 0) {
+        if (strcmp (argv[0], "-b") == 0) {
+            bbox = TRUE;
+            argc--;
+            argv++;
+        } else if (strcmp (argv[0], "-s") == 0) {
+            standard_svg = TRUE;
+            argc--;
+            argv++;
+        } else if (strcmp (argv[0], "-g") == 0) {
+            if (argc > 1) {
+                glyph_id = argv[1];
+                argc -= 2;
+                argv += 2;
+            } else {
+                usage_and_exit();
+            }
+        } else if (strcmp (argv[0], "-e") == 0) {
+            if (argc > 1) {
+                em_size = atoi (argv[1]);
+                if (em_size <= 0) {
+                    usage_and_exit();
+                }
+                argc -= 2;
+                argv += 2;
+            } else {
+                usage_and_exit();
+            }
+        } else {
+            input_file = argv[0];
+            argc--;
+            argv++;
+        }
+    }
+    if (!input_file)
+        usage_and_exit();
+
+    svg_document = read_file (input_file);
+    if (!svg_document) {
+        printf("error reading file %s\n", input_file);
+        exit (1);
+    }
+
+    output_file = create_output_name (input_file, CAIRO_SVG);
+    render_svg (svg_document, CAIRO_SVG, output_file);
+    free (output_file);
+
+    output_file = create_output_name (input_file, LIBRSVG);
+    render_svg (svg_document, LIBRSVG, output_file);
+    free (output_file);
+
+    free (svg_document);
+
+    return 0;
+}
diff --git a/test/svg/transform.0.translate.svg b/test/svg/transform.0.translate.svg
new file mode 100644
index 000000000..5d9555673
--- /dev/null
+++ b/test/svg/transform.0.translate.svg
@@ -0,0 +1,8 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <use xlink:href="#square" transform="translate(600, -400)"/>
+</svg>
diff --git a/test/svg/transform.1.scale.svg b/test/svg/transform.1.scale.svg
new file mode 100644
index 000000000..b7742fc80
--- /dev/null
+++ b/test/svg/transform.1.scale.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <g transform="translate(400, -400)">
+    <use xlink:href="#square" transform="scale(1.5, 0.5)"/>
+  </g>
+</svg>
diff --git a/test/svg/transform.2.rotate.svg b/test/svg/transform.2.rotate.svg
new file mode 100644
index 000000000..926ae63fc
--- /dev/null
+++ b/test/svg/transform.2.rotate.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <g transform="translate(600, -600)">
+    <use xlink:href="#square" transform="rotate(30)"/>
+  </g>
+</svg>
diff --git a/test/svg/transform.3.skewX.svg b/test/svg/transform.3.skewX.svg
new file mode 100644
index 000000000..f7555534d
--- /dev/null
+++ b/test/svg/transform.3.skewX.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <g transform="translate(400, -400)">
+    <use xlink:href="#square" transform="skewX(30)"/>
+  </g>
+</svg>
diff --git a/test/svg/transform.4.skewY.svg b/test/svg/transform.4.skewY.svg
new file mode 100644
index 000000000..cd62c8a8a
--- /dev/null
+++ b/test/svg/transform.4.skewY.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <g transform="translate(400, -500)">
+    <use xlink:href="#square" transform="skewY(30)"/>
+  </g>
+</svg>
diff --git a/test/svg/transform.5.matrix.svg b/test/svg/transform.5.matrix.svg
new file mode 100644
index 000000000..643fbe36b
--- /dev/null
+++ b/test/svg/transform.5.matrix.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="square" width="300" height="300"/>
+  </defs>
+  <use xlink:href="#square" transform="translate(100, -900)"/>
+  <g transform="translate(600, -600)">
+    <use xlink:href="#square" transform="matrix(1, 0.4, -0.6, 1.1, 50, -70)"/>
+  </g>
+</svg>
diff --git a/test/svg/transform.6.multiple.svg b/test/svg/transform.6.multiple.svg
new file mode 100644
index 000000000..90c7b3054
--- /dev/null
+++ b/test/svg/transform.6.multiple.svg
@@ -0,0 +1,16 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <path id="heart" d="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z" />
+  </defs>
+  <use xlink:href="#heart"
+       transform="translate(10, -650)
+                  rotate(-10 50 100)
+                  translate(-166 125.5)
+                  skewX(40)
+                  scale(1 0.5),scale(8, 8)"
+       fill="grey"/>
+  <use xlink:href="#heart"
+       transform="translate(300, -800),scale(6, 6)"
+       fill="none" stroke="red" />
+</svg>
diff --git a/test/svg/transform.7.stroke.svg b/test/svg/transform.7.stroke.svg
new file mode 100644
index 000000000..f56b24dfe
--- /dev/null
+++ b/test/svg/transform.7.stroke.svg
@@ -0,0 +1,20 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <circle id="c1"
+            cx="100" cy="100" r="80"
+            fill="none"
+            stroke="black"
+            stroke-width="20"  />
+    <circle id="c2"
+          cx="100" cy="100" r="80"
+          fill="none"
+          stroke="black"
+          stroke-width="20"
+          transform="scale(3,3)" />
+  </defs>
+
+  <use xlink:href="#c1" x="100" y="-900"/>
+  <use xlink:href="#c2" x="300" y="-700"/>
+  
+</svg>
commit 252ff60f1ee62c5b36d650b91ac2536d03656bea
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Jun 15 07:05:52 2022 +0930

    SVG font test using cairo logo

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5f3ff2396..fa7403b35 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -94,6 +94,7 @@ fedora image:
       dejavu-sans-mono-fonts
       dejavu-serif-fonts
       google-noto-emoji-color-fonts
+      fonttools
 
 fedora autotools build:
   extends:
diff --git a/meson.build b/meson.build
index 831527c43..02f74856c 100644
--- a/meson.build
+++ b/meson.build
@@ -302,6 +302,8 @@ if fontconfig_dep.found()
   }]
 endif
 
+ttx = find_program('ttx', required: false)
+
 freetype_dep = dependency('freetype2',
   required: get_option('freetype'),
   version: freetype_required_version,
@@ -339,6 +341,9 @@ if freetype_dep.found()
     endif
     if cc.has_type('FT_SVG_Document', dependencies: freetype_dep, prefix: '#include <freetype/otsvg.h>')
       conf.set('HAVE_FT_SVG_DOCUMENT', 1)
+      if ttx.found()
+        conf.set('CAIRO_CAN_TEST_TTX_FONT', 1)
+      endif
     endif
     check_funcs += ft_check_funcs
     deps += [freetype_dep]
diff --git a/test/cairo-logo-font.ttx b/test/cairo-logo-font.ttx
new file mode 100644
index 000000000..9e4b832c0
--- /dev/null
+++ b/test/cairo-logo-font.ttx
@@ -0,0 +1,539 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x3e7355ef"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000001"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 15 00:00:00 2022"/>
+    <modified value="Wed Jun 15 00:00:00 2022"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="1000"/>
+    <yMax value="1000"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="1000"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="2"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="2"/>
+    <maxPoints value="1"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="1"/>
+    <maxCompositeContours value="1"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="256"/>
+    <maxSizeOfInstructions value="1"/>
+    <maxComponentElements value="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="1000"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000001"/>
+    <ySubscriptXSize value="600"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="600"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="300"/>
+    <yStrikeoutSize value="0"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="djr "/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="65"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-100"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="720"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="0"/>
+    <mtx name="A" width="1000" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="A">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="0" y="1000" on="1"/>
+        <pt x="1000" y="1000" on="1"/>
+        <pt x="1000" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Cairo Logo
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Cairo Logo Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+
+  <SVG>
+
+    <svgDoc endGlyphID="0" startGlyphID="0">
+      <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"></svg>]]>
+    </svgDoc>
+    <svgDoc endGlyphID="1" startGlyphID="1">
+      <![CDATA[<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="310" height="508">
+	<defs id="cairo-artwork_defs">
+		<g id="hacker_emblem">
+			<!-- Note: This is similar though not identical to Keith Packard's SVG version
+				of the hacker emblem (http://www.catb.org/hacker-emblem/glider.svg) -->
+			<g id="hacker_emblem_grid" fill="white" stroke="none">
+				<!-- Outside: Top, Right, Bottom, Left -->
+				<rect x="-2.95" y="-3.05" width="6"   height="0.1" />
+				<rect x="2.95"  y="-2.95" width="0.1" height="6" />
+				<rect x="-3.05" y="2.95"  width="6"   height="0.1" />
+				<rect x="-3.05" y="-3.05" width="0.1" height="6" />
+				<!-- Vertical: Left, Right -->
+				<rect x="-1.05" y="-2.95" width="0.1" height="5.9" />
+				<rect x="0.95"  y="-2.95" width="0.1" height="5.9" />
+				<!-- Horizontal: TopLeft, TopMiddle, TopRight -->
+				<rect x="-2.95" y="-1.05" width="1.9" height="0.1" />
+				<rect x="-0.95" y="-1.05" width="1.9" height="0.1" />
+				<rect x="1.05"  y="-1.05" width="1.9" height="0.1" />
+				<!-- Horizontal: BottomLeft, BottomMiddle, BottomRight -->
+				<rect x="-2.95" y="0.95"  width="1.9" height="0.1" />
+				<rect x="-0.95" y="0.95"  width="1.9" height="0.1" />
+				<rect x="1.05"  y="0.95"  width="1.9" height="0.1" />
+			</g>
+			<g id="hacker_emblem_dots" fill="white">
+				<circle cx="0"  cy="-2" r="0.7" />
+				<circle cx="2"  cy="0"  r="0.7" />
+				<circle cx="-2" cy="2"  r="0.7" />
+				<circle cx="0"  cy="2"  r="0.7" />
+				<circle cx="2"  cy="2"  r="0.7" />
+			</g>
+		</g>
+		<g id="scarab" fill="#3B80AE">
+			<g transform="translate(-150, -170)">
+			<path id="scarab_head" d="M205.599,94.567c0-11.668-24.914-21.129-55.628-21.129
+				c-30.723,0-55.624,9.46-55.624,21.129c0,10.203,24.901,7.346,55.624,7.346C180.685,101.913,205.599,104.233,205.599,94.567z"/>
+			<path id="scarab_torso" d="M136.423,161.506c0,0,12.751,12.577,13.547,13.362
+				c2.262-2.232,13.545-13.362,13.545-13.362c7.135-7.036,87.111-6.399,91.066-6.363c-0.469-6.298-1.254-12.472-2.325-18.519
+				c-15.183-19.279-42.811-32.225-74.485-32.225h-55.518c-31.745,0-59.439,13.011-74.598,32.37c-1.054,6-1.829,12.128-2.296,18.374
+				C49.321,155.106,129.288,154.47,136.423,161.506z"/>
+			<path id="scarab_spine" d="M149.97,301.187c2.005-24.729,8.386-103.483,8.405-103.721
+				c-0.09-0.219-6.478-15.578-8.405-20.214c-1.936,4.655-8.316,19.995-8.408,20.214C141.582,197.704,147.965,276.458,149.97,301.187z"/>
+			<path id="scarab_wing_left" d="M140.403,197.149l8.862-21.31l-13.686-13.499
+				c-5.65-5.573-67.074-6.235-90.259-6.019l-0.006-0.622c-0.154,2.144-0.271,4.302-0.35,6.475
+				c-0.076,2.207,10.392,4.706,10.392,6.717c0,2.319-10.457,5.084-10.359,7.631c2.993,73.349,48.53,131.631,104.372,132.048
+				l-9.02-111.29L140.403,197.149z"/>
+			<path id="scarab_wing_right" d="M244.585,168.891c0-2.011,10.467-4.506,10.391-6.715
+				c-0.079-2.174-0.195-4.332-0.351-6.479l-0.004,0.624c-23.186-0.216-84.608,0.445-90.26,6.017l-13.688,13.502l8.915,21.438
+				l-9.017,111.29c55.854-0.417,101.378-58.698,104.373-132.049C255.04,173.976,244.585,171.209,244.585,168.891z"/>
+			<path id="scarab_leg_front_left" d="M44.506,141.12c-4.135-0.856-4.895-1.54-7.935-2.92
+				c-9.59-3.364-10.376-5.481-16.08-11.86c-7.426-8.306-12.661-20.142-17.1-29.463c-3.576-7.525-3.984-16.409-2.86-24.273
+				c0.991-6.935,7.144-12.869,12.074-18.92c5.844-7.191,10.356-14.822,17.924-21.354c7.736-6.682,23.203-9.809,26.168-19.648
+				C57.86,8.819,54.334,1.766,61.482,0c-0.366,4.703,3.639,8.477,2.397,13.575c-1.129,4.627-4.368,5.811-9.611,9.099
+				c-7.564,4.746-18.366,8.779-24.748,13.965c-7.175,5.827-4.369,13.771-10.569,20.057c-2.001,2.03-7.901,4.706-9.137,6.83
+				c-1.861,3.199-0.297,9.572-0.116,13.12c0.425,8.284,5.588,14.244,9.555,22.045c4.152,8.141,6.429,15.409,13.411,22.519
+				c4.183,4.262,11.429,4.802,16.21,10.647l-3.555,4.186L44.506,141.12z"/>
+			<path id="scarab_leg_middle_left" d="M43.94,191.922l-0.809-7.346
+				c-9.506-4.579-10.339-9.772-20.738-12.466c-23.728-6.151-21.361,11.25-15.532,26.373c5.676,14.726,8.237,30.23,14.345,44.795
+				c2.805,6.688,6.919,13.213,14.298,15.127c0.372-8.435-0.917-10.651-6.113-16.919c-4.395-5.293-3.326-12.548-6.072-18.504
+				c-3.581-7.804-4.196-15.646-7.279-23.502c-1.363-3.479-8.33-13.966-6.452-17.861c3.183-6.603,9.178-0.083,12.179,2.077
+				c4.218,3.036,6.467,2.223,11.681,2.898C34.041,186.673,37.005,188.756,43.94,191.922z"/>
+			<path id="scarab_leg_back_left" d="M65.839,257.063l-2.771-4.837
+				c-6.68,8.928-6.993,16.228-10.056,23.347c-5.277,12.263-0.157,28.851,9.854,37.676c6.052,5.375,15.907,9.618,23.122,13.136
+				c10.035,4.892,20.113,11.286,31.336,13.396c2.482,0.466,8.798,1.295,6.693-3.522c-0.975-2.237-8.091-4.591-10.146-5.734
+				c-8.312-4.623-16.377-10.524-24.142-16.176c-9.498-6.862-20.843-11.186-28.311-20.684c-3.054-3.885-3.544-4.922-2.816-9.39
+				c0.693-4.263,1.344-9.174,2.241-13.439C61.855,266.029,63.274,261.378,65.839,257.063z"/>
+			<path id="scarab_leg_front_right" d="M255.487,141.12c4.134-0.856,4.896-1.54,7.936-2.92
+				c9.583-3.364,10.369-5.481,16.071-11.86c7.428-8.306,12.661-20.142,17.115-29.463c3.574-7.525,3.983-16.409,2.86-24.273
+				c-0.992-6.935-7.157-12.869-12.087-18.92c-5.843-7.191-10.356-14.822-17.919-21.354c-7.735-6.682-23.202-9.809-26.167-19.648
+				C242.135,8.819,245.66,1.766,238.511,0c0.366,4.703-3.637,8.477-2.396,13.575c1.131,4.627,4.368,5.811,9.611,9.099
+				c7.563,4.746,18.367,8.779,24.747,13.965c7.17,5.827,4.362,13.771,10.563,20.057c2.001,2.03,7.901,4.706,9.139,6.83
+				c1.859,3.199,0.295,9.572,0.113,13.12c-0.424,8.284-5.588,14.244-9.553,22.045c-4.152,8.141-6.431,15.409-13.404,22.519
+				c-4.184,4.262-11.429,4.802-16.211,10.647l3.556,4.186L255.487,141.12z"/>
+			<path id="scarab_leg_middle_right" d="M256.053,191.922l0.81-7.346
+				c9.507-4.579,10.34-9.772,20.73-12.466c23.741-6.151,21.374,11.25,15.534,26.373c-5.676,14.726-8.238,30.23-14.347,44.795
+				c-2.804,6.688-6.911,13.213-14.291,15.127c-0.371-8.435,0.918-10.651,6.113-16.919c4.39-5.293,3.319-12.548,6.066-18.504
+				c3.58-7.804,4.197-15.646,7.278-23.502c1.363-3.479,8.33-13.966,6.453-17.861c-3.184-6.603-9.179-0.083-12.181,2.077
+				c-4.217,3.036-6.458,2.223-11.672,2.898C265.951,186.673,262.986,188.756,256.053,191.922z"/>
+			<path id="scarab_leg_back_right" d="M234.155,257.063l2.771-4.837
+				c6.679,8.928,6.991,16.228,10.057,23.347c5.274,12.263,0.154,28.851-9.854,37.676c-6.055,5.375-15.903,9.618-23.117,13.136
+				c-10.034,4.892-20.127,11.286-31.351,13.396c-2.481,0.466-8.789,1.295-6.691-3.522c0.976-2.237,8.092-4.591,10.146-5.734
+				c8.312-4.623,16.392-10.524,24.155-16.176c9.498-6.862,20.838-11.186,28.305-20.684c3.055-3.885,3.543-4.922,2.818-9.39
+				c-0.696-4.263-1.346-9.174-2.244-13.439C238.137,266.029,236.718,261.378,234.155,257.063z"/>
+			</g>
+		</g>
+		<radialGradient id="gradient_radial_dung"
+				cx="0" cy="0" r="60"
+				fx="0" fy="0" gradientUnits="userSpaceOnUse"
+		>
+			<stop offset="0"    stop-color="#9a9a9a" />
+			<stop offset="0.70" stop-color="#bababa" />
+			<stop offset="0.95" stop-color="#FFFFFF" />
+		</radialGradient>
+		<g id="dung">
+			<circle cx="0" cy="0" r="60" fill="url(#gradient_radial_dung)" />
+			<g transform="translate(-61, -61)">
+				<!-- rough equivalent: <circle cx="0" cy="0" r="60" stroke="#8a8a8a" stroke-width="2" /> -->
+				<path fill="#8a8a8a" d="M0,61c0,33.636,27.364,61,61,61s61-27.364,61-61S94.636,0,61,0S0,27.364,0,61z
+							M2,61C2,28.467,28.467,2,61,2c32.532,0,59,26.467,59,59c0,32.533-26.468,59-59,59C28.467,120,2,93.533,2,61z"/>
+			</g>
+			<use xlink:href="#hacker_emblem" x="0" y="0" transform="scale(9)" />
+		</g>
+
+		<!-- scarab dimensions: 300x340 -->
+		<!-- dung dimensions: 120x120 (radius: 60) -->
+		<!-- scarab and dung dimensions: 300x400 -->
+
+		<g id="cairo_logo">
+			<!-- dimensions: 300x400, centered -->
+			<!-- The logo (scarab and dung), with the center-point of the bounding box at (0,0) -->
+			<use xlink:href="#dung" x="0" y="0" transform="translate(0, -140)" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(0, 30)" />
+		</g>
+		<g id="cairo_logo_dung-centered">
+			<!-- The logo (scarab and dung), with the dung at (0,0), the scarab below -->
+			<use xlink:href="#dung" x="0" y="0" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(0,170)" />
+		</g>
+		<g id="cairo_logo_scarab-centered">
+			<!-- The logo (scarab and dung), with the scarab's rotational center at (0,0), the dung above -->
+			<!-- The scarab's rotational center in this case is not the center of its bounding box,
+				but is calculated to be the intersection-point of the torso, spine and wings -->
+			<use xlink:href="#dung" x="0" y="0" transform="translate(0, -175.85)" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(0, -5.85)" />
+		</g>
+		<g id="cairo_logo_top-centered">
+			<!-- The logo (scarab and dung), with the top-center point of the bounding box at (0,0) -->
+			<use xlink:href="#dung" x="0" y="0" transform="translate(0, 60)" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(0, 230)" /><!-- (0,170+60) -->
+		</g>
+		<g id="cairo_logo_bottom-centered">
+			<!-- The logo (scarab and dung), with the bottom-center point of the bounding box at (0,0) -->
+			<use xlink:href="#dung" x="0" y="0" transform="translate(0, -340)" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(0, -170)" />
+		</g>
+		<g id="cairo_logo_right-centered">
+			<!-- The logo (scarab and dung), with the right-center point of the bounding box at (0,0) -->
+			<use xlink:href="#dung" x="0" y="0" transform="translate(-150, -140)" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 30)" />
+		</g>
+		<g id="cairo_logo_left-centered">
+			<!-- The logo (scarab and dung), with the left-center point of the bounding box at (0,0) -->
+			<use xlink:href="#dung" x="0" y="0" transform="translate(150, -140)" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(150, 30)" />
+		</g>
+		<g id="cairo_logo_topleft-centered">
+			<!-- The logo (scarab and dung), with the top-left point of the bounding box at (0,0) -->
+			<use xlink:href="#dung" x="0" y="0" transform="translate(150, 60)" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(150, 230)" /><!-- (150, 170+60) -->
+		</g>
+		<g id="cairo_logo_topright-centered">
+			<!-- The logo (scarab and dung), with the top-right point of the bounding box at (0,0) -->
+			<use xlink:href="#dung" x="0" y="0" transform="translate(-150, 60)" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 230)" /><!-- (-150,170+60) -->
+		</g>
+		<g id="cairo_logo_bottomleft-centered">
+			<!-- The logo (scarab and dung), with the bottom-left point of the bounding box at (0,0) -->
+			<use xlink:href="#dung" x="0" y="0" transform="translate(150, -340)" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(150, -170)" />
+		</g>
+		<g id="cairo_logo_bottomright-centered">
+			<!-- The logo (scarab and dung), with the bottom-right point of the bounding box at (0,0) -->
+			<use xlink:href="#dung" x="0" y="0" transform="translate(-150, -340)" />
+			<use xlink:href="#scarab" x="0" y="0" transform="translate(-150, -170)" />
+		</g>
+
+		<g id="cairo_text" transform="translate(0,-97)">
+		    <g transform="scale(0.1484,0.1484)"> <g transform="translate(-1139,-208.5)">
+			 <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+			 <path transform="translate(65,0)" d="
+			       M 412, 433
+			       C 385, 422 336, 413 298, 413
+			       C 142, 413 38, 525 38, 680
+			       C 38, 826 144, 947 298, 947
+			       C 332, 947 377, 944 416, 926
+			       L 409, 842
+			       C 380, 861 340, 871 308, 871
+			       C 187, 871 138, 771 138, 680
+			       C 138, 583 197, 489 302, 489
+			       C 332, 489 368, 496 404, 511
+			       L 412, 433 " />
+			 <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+			 <path transform="translate(486.75,0)" d="
+			       M 109, 541
+			       C 147, 509 204, 489 257, 489
+			       C 351, 489 383, 534 383, 622
+			       C 346, 620 320, 620 283, 620
+			       C 186, 620 46, 660 46, 788
+			       C 46, 899 123, 947 233, 947
+			       C 319, 947 369, 900 391, 869
+			       L 393, 869
+			       L 393, 935
+			       L 481, 935
+			       C 479, 920 477, 893 477, 835
+			       L 477, 624
+			       C 477, 485 418, 413 272, 413
+			       C 207, 413 151, 433 104, 461
+			       L 109, 541
+			       M 383, 737
+			       C 383, 813 334, 871 241, 871
+			       C 198, 871 146, 842 146, 788
+			       C 146, 698 272, 690 323, 690
+			       C 343, 690 363, 692 383, 692
+			       L 383, 737 " />
+			 <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+			 <path transform="translate(1000,0)" d="
+			       M 92, 935
+			       L 186, 935
+			       L 186, 425
+			       L 92, 425
+			       L 92, 935
+			       M 88, 261
+			       A 51, 51 0 1 1 190,261
+			       A 51, 51 0 1 1 88,261" />
+			 <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+			 <path transform="translate(1234.25,0)" d="
+			       M 80, 935
+			       L 174, 935
+			       L 174, 703
+			       C 174, 575 229, 495 313, 495
+			       C 329, 495 348, 497 365, 504
+			       L 365, 420
+			       C 345, 416 331, 413 303, 413
+			       C 249, 413 195, 451 170, 504
+			       L 168, 504
+			       L 168, 425
+			       L 80, 425
+			       L 80, 935 " />
+			 <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+			 <path transform="translate(1610,0)" d="
+			       M 46, 680
+			       C 46, 826 152, 947 306, 947
+			       C 459, 947 565, 826 565, 680
+			       C 565, 525 461, 413 306, 413
+			       C 150, 413 46, 525 46, 680
+			       M 146, 680
+			       C 146, 583 205, 489 306, 489
+			       C 406, 489 465, 583 465, 680
+			       C 465, 771 416, 871 306, 871
+			       C 195, 871 146, 771 146, 680 " />
+			 <!-- bounds: 38, 205 <-> 2232, 947 -->
+		    </g> </g>
+		</g>
+
+		<!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+		<g id="cairo_text_small" transform="translate(0,-71)">
+		    <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+			 <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+			 <path transform="translate(-151,0)" d="
+			       M 412, 433
+			       C 385, 422 336, 413 298, 413
+			       C 142, 413 38, 525 38, 680
+			       C 38, 826 144, 947 298, 947
+			       C 332, 947 377, 944 416, 926
+			       L 409, 842
+			       C 380, 861 340, 871 308, 871
+			       C 187, 871 138, 771 138, 680
+			       C 138, 583 197, 489 302, 489
+			       C 332, 489 368, 496 404, 511
+			       L 412, 433 " />
+			 <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+			 <path transform="translate(379.5,0)" d="
+			       M 109, 541
+			       C 147, 509 204, 489 257, 489
+			       C 351, 489 383, 534 383, 622
+			       C 346, 620 320, 620 283, 620
+			       C 186, 620 46, 660 46, 788
+			       C 46, 899 123, 947 233, 947
+			       C 319, 947 369, 900 391, 869
+			       L 393, 869
+			       L 393, 935
+			       L 481, 935
+			       C 479, 920 477, 893 477, 835
+			       L 477, 624
+			       C 477, 485 418, 413 272, 413
+			       C 207, 413 151, 433 104, 461
+			       L 109, 541
+			       M 383, 737
+			       C 383, 813 334, 871 241, 871
+			       C 198, 871 146, 842 146, 788
+			       C 146, 698 272, 690 323, 690
+			       C 343, 690 363, 692 383, 692
+			       L 383, 737 " />
+			 <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+			 <path transform="translate(1000,0)" d="
+			       M 92, 935
+			       L 186, 935
+			       L 186, 425
+			       L 92, 425
+			       L 92, 935
+			       M 88, 261
+			       A 51, 51 0 1 1 190,261
+			       A 51, 51 0 1 1 88,261" />
+			 <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+			 <path transform="translate(1341.5,0)" d="
+			       M 80, 935
+			       L 174, 935
+			       L 174, 703
+			       C 174, 575 229, 495 313, 495
+			       C 329, 495 348, 497 365, 504
+			       L 365, 420
+			       C 345, 416 331, 413 303, 413
+			       C 249, 413 195, 451 170, 504
+			       L 168, 504
+			       L 168, 425
+			       L 80, 425
+			       L 80, 935 " />
+			 <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+			 <path transform="translate(1826,0)" d="
+			       M 46, 680
+			       C 46, 826 152, 947 306, 947
+			       C 459, 947 565, 826 565, 680
+			       C 565, 525 461, 413 306, 413
+			       C 150, 413 46, 525 46, 680
+			       M 146, 680
+			       C 146, 583 205, 489 306, 489
+			       C 406, 489 465, 583 465, 680
+			       C 465, 771 416, 871 306, 871
+			       C 195, 871 146, 771 146, 680 " />
+			 <!-- bounds: 38, 205 <-> 2232, 947 -->
+		    </g> </g>
+		</g>
+
+		<g id="cairo_logo_with_text">
+			<!-- The logo (scarab and dung), with the text 'cairo' below, the dot of the 'i' positioned between the hind legs of the scarab -->
+			<!-- dimensions: 300x490, centered -->
+			<use xlink:href="#cairo_logo_top-centered" transform="translate(0, -245)" />
+			<use xlink:href="#cairo_text" transform="translate(0, 245)" />
+		</g>
+
+		<g id="cairo_banner">
+			<!-- The logo on the left, the text 'cairo' in the center, and a mirror image of the logo on the right -->
+			<!-- The logos are scaled such that the scarab body nearly matches the height of the text characters (excepting the 'i')
+				and the dung should nearly aligns with the dot of the 'i'. The bottoms of the logos are aligned with the bottom of the text. -->
+			<!-- dimensions: 370x88, centered -->
+			<use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(-180, 40), scale(0.1944)" />
+			<use xlink:href="#cairo_text_small" transform="translate(0, 42)" fill="black" />
+			<use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(180, 40), scale(0.1944), scale(-1, 1)" />
+		</g>
+	</defs>
+	<g transform="translate(5, -500)">
+		<use xlink:href="#cairo_logo_with_text" transform="translate(150, 245)" />
+	</g>
+</svg>]]>
+    </svgDoc>
+    <colorPalettes>
+    </colorPalettes>
+  </SVG>
+
+</ttFont>
diff --git a/test/cairo-test.c b/test/cairo-test.c
index cbd4fb1ab..5a2cf1a74 100644
--- a/test/cairo-test.c
+++ b/test/cairo-test.c
@@ -1825,3 +1825,70 @@ cairo_test_status_from_status (const cairo_test_context_t *ctx,
 
     return CAIRO_TEST_FAILURE;
 }
+
+#if CAIRO_HAS_FT_FONT
+
+#include "cairo-ft.h"
+
+static void
+_free_face (void *face)
+{
+    FT_Done_Face ((FT_Face) face);
+}
+
+static FT_Library ft_library = NULL;
+
+#endif
+
+static const cairo_user_data_key_t ft_font_key;
+
+cairo_test_status_t
+cairo_test_ft_select_font_from_file (cairo_t      *cr,
+                                     const char   *filename)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+#if CAIRO_HAS_FT_FONT
+    FT_Face face;
+    cairo_font_face_t *font_face;
+    char *srcdir_filename = NULL;
+
+    if (access (filename, F_OK) != 0) {
+	if (ctx->srcdir) {
+	    xasprintf (&srcdir_filename, "%s/%s", ctx->srcdir, filename);
+            filename = srcdir_filename;
+	}
+    }
+
+    if (access (filename, F_OK) != 0) {
+        cairo_test_log (ctx, "Could not find font file: %s\n", filename);
+        return CAIRO_TEST_FAILURE;
+    }
+
+    if (!ft_library) {
+        if (FT_Init_FreeType (&ft_library))
+            return CAIRO_TEST_FAILURE;
+    }
+
+    if (FT_New_Face (ft_library, filename, 0, &face)) {
+        cairo_test_log (ctx, "FT_New_Face failed loading font file: %s\n", filename);
+        return CAIRO_TEST_FAILURE;
+    }
+
+    free (srcdir_filename);
+    font_face = cairo_ft_font_face_create_for_ft_face (face, 0);
+    if (cairo_font_face_status (font_face))
+        return CAIRO_TEST_FAILURE;
+
+    cairo_font_face_set_user_data (font_face, &ft_font_key, face, _free_face);
+    cairo_set_font_face (cr, font_face);
+    if (cairo_status (cr))
+        return CAIRO_TEST_FAILURE;
+
+    cairo_font_face_destroy (font_face);
+
+    return CAIRO_TEST_SUCCESS;
+#else
+    cairo_test_log (ctx, "cairo_test_ft_select_font_from_file() requires the FreeType backend\n");
+    return CAIRO_TEST_FAILURE;
+#endif
+}
diff --git a/test/cairo-test.h b/test/cairo-test.h
index 6169c5371..adff2583c 100644
--- a/test/cairo-test.h
+++ b/test/cairo-test.h
@@ -323,6 +323,11 @@ cairo_t *
 cairo_test_create (cairo_surface_t *surface,
 		   const cairo_test_context_t *ctx);
 
+/* Set font face from a font file in build or src dir, using the FT backend. */
+cairo_test_status_t
+cairo_test_ft_select_font_from_file (cairo_t      *cr,
+                                     const char   *filename);
+
 CAIRO_END_DECLS
 
 #endif
diff --git a/test/ft-svg-cairo-logo.c b/test/ft-svg-cairo-logo.c
new file mode 100644
index 000000000..22fb25f23
--- /dev/null
+++ b/test/ft-svg-cairo-logo.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2022 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairo-test.h"
+#include <cairo-ft.h>
+
+#define FONT_SIZE 200
+#define MARGIN 5
+#define WIDTH  (FONT_SIZE + MARGIN*2)
+#define HEIGHT (FONT_SIZE + MARGIN*2)
+
+#define FONT_FILE "cairo-logo-font.ttf"
+
+/* Character code in font of the logo */
+#define CAIRO_LOGO_CHAR "A"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_test_status_t result;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    result = cairo_test_ft_select_font_from_file (cr, FONT_FILE);
+    if (result)
+        return result;
+
+    cairo_set_font_size (cr, FONT_SIZE);
+    cairo_move_to (cr, MARGIN, FONT_SIZE + MARGIN);
+
+    cairo_show_text (cr, CAIRO_LOGO_CHAR);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (ft_svg_cairo_logo,
+	    "Test cairo logo SVG font",
+	    "svgrender", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw)
diff --git a/test/meson.build b/test/meson.build
index 7d6d6e11b..3071ac6ca 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -429,6 +429,10 @@ test_ft_svg_font_sources = [
   'ft-svg-color-font.c',
 ]
 
+test_ft_svg_ttx_font_sources = [
+  'ft-svg-cairo-logo.c',
+]
+
 test_gl_sources = [
   'gl-device-release.c',
   'gl-oversized-surface.c',
@@ -519,6 +523,10 @@ ps2png_sources = [
   'ps2png.c',
 ]
 
+test_ttx_fonts = [
+  'cairo-logo-font.ttx',
+]
+
 build_any2ppm = false
 has_multipage_surfaces = false
 add_fallback_resolution = false
@@ -531,6 +539,9 @@ if feature_conf.get('CAIRO_HAS_FT_FONT', 0) == 1 and feature_conf.get('CAIRO_HAS
   test_sources += test_ft_font_sources
   if conf.get('HAVE_FT_SVG_DOCUMENT', 0) == 1
     test_sources += test_ft_svg_font_sources
+    if conf.get('CAIRO_CAN_TEST_TTX_FONT', 0) == 1
+      test_sources += test_ft_svg_ttx_font_sources
+    endif
   endif
 endif
 
@@ -644,10 +655,22 @@ endforeach
 if build_machine.system() != 'windows'
   run_command('ln', '-sf',
               meson.current_source_dir(),
-              join_paths(meson.current_build_dir(), 'srcdir'),
+              meson.current_build_dir() / 'srcdir',
               check: true)
 endif
 
+if ttx.found()
+  # By default, if the output file exists, ttx creates a new name. We specify the full
+  # output name to make ttx overwrite the existing file instead of creating a new file.
+  foreach ttx_font : test_ttx_fonts
+    custom_target(ttx_font,
+                  input: ttx_font,
+                  command: [ ttx, '-q', '-o', '@OUTDIR@' / '@BASENAME at .ttf', '@INPUT@' ],
+                  output: '@BASENAME at .ttf',
+                  build_by_default: true)
+  endforeach
+endif
+
 env = environment()
 
 env.set('srcdir', meson.current_source_dir())
diff --git a/test/reference/ft-svg-cairo-logo.ref.png b/test/reference/ft-svg-cairo-logo.ref.png
new file mode 100644
index 000000000..0c92934d6
Binary files /dev/null and b/test/reference/ft-svg-cairo-logo.ref.png differ
commit 1bd5751324866a2bfbb084fcb7d5462d983a878e
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Tue Jun 14 22:01:04 2022 +0930

    FT SVG color font test

diff --git a/configure.ac b/configure.ac
index 0420fba0c..ebca4fd10 100644
--- a/configure.ac
+++ b/configure.ac
@@ -468,7 +468,8 @@ if test "x$use_ft" = "xyes"; then
 
   AC_CHECK_FUNCS(FT_Get_X11_Font_Format FT_GlyphSlot_Embolden FT_GlyphSlot_Oblique FT_Load_Sfnt_Table FT_Library_SetLcdFilter FT_Get_Var_Design_Coordinates FT_Done_MM_Var FT_Palette_Select)
 
-  AC_CHECK_TYPES([FT_SVG_Document], [], [], [[#include <freetype/otsvg.h>]])
+  AC_CHECK_TYPES([FT_SVG_Document], [have_ft_svg=yes], [have_ft_svg=no], [[#include <freetype/otsvg.h>]])
+  AM_CONDITIONAL(HAVE_FT_SVG_DOCUMENT, test "x$have_ft_svg=" = "xyes")
 
   AC_MSG_CHECKING(for FT_HAS_COLOR)
   AC_LINK_IFELSE([AC_LANG_PROGRAM([
diff --git a/test/Makefile.am b/test/Makefile.am
index a74d71a7c..df2d59f44 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -14,6 +14,9 @@ if CAIRO_HAS_FT_FONT
 test_sources += $(ft_font_test_sources)
 if CAIRO_HAS_FC_FONT
 test_sources += $(fc_font_test_sources)
+if HAVE_FT_SVG_DOCUMENT
+test_sources += $(fc_svg_font_test_sources)
+endif
 endif
 endif
 
diff --git a/test/Makefile.sources b/test/Makefile.sources
index 330aa9da1..107f1b7d0 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -423,6 +423,9 @@ fc_font_test_sources = \
 	ft-text-vertical-layout-type3.c \
 	ft-text-antialias-none.c
 
+fc_svg_font_test_sources = \
+	ft-svg-color-font.c
+
 gl_surface_test_sources = \
 	gl-device-release.c \
 	gl-oversized-surface.c \
diff --git a/test/ft-svg-color-font.c b/test/ft-svg-color-font.c
new file mode 100644
index 000000000..56510feca
--- /dev/null
+++ b/test/ft-svg-color-font.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright © 2022 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairo-test.h"
+#include <cairo-ft.h>
+
+#define FONT_SIZE 50
+#define MARGIN 5
+#define WIDTH  (FONT_SIZE*4 + MARGIN*2)
+#define HEIGHT (FONT_SIZE*3 + MARGIN*5)
+
+/* Check the full name to ensure we got an SVG font. */
+#define FONT_FAMILY "Twitter Color Emoji"
+#define FONT_FULLNAME "Twitter Color Emoji SVGinOT"
+
+static const char spade_utf8[] =   { 0xe2, 0x99, 0xa0, 0x00 }; /* U+2660 glyph 87 */
+static const char club_utf8[] =    { 0xe2, 0x99, 0xa3, 0x00 }; /* U+2663 glyph 88 */
+static const char heart_utf8[] =   { 0xe2, 0x99, 0xa5, 0x00 }; /* U+2665 glyph 89 */
+static const char diamond_utf8[] = { 0xe2, 0x99, 0xa6, 0x00 }; /* U+2666 glyph 90 */
+
+static cairo_test_status_t
+set_color_emoji_font (cairo_t *cr)
+{
+    cairo_font_options_t *font_options;
+    cairo_font_face_t *font_face;
+    FcPattern *pattern;
+    FcPattern *resolved;
+    FcChar8 *font_name;
+    FcResult result;
+
+    pattern = FcPatternCreate ();
+    if (pattern == NULL)
+	return CAIRO_TEST_NO_MEMORY;
+
+    FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *) FONT_FAMILY);
+    FcConfigSubstitute (NULL, pattern, FcMatchPattern);
+
+    font_options = cairo_font_options_create ();
+    cairo_get_font_options (cr, font_options);
+    cairo_ft_font_options_substitute (font_options, pattern);
+
+    FcDefaultSubstitute (pattern);
+    resolved = FcFontMatch (NULL, pattern, &result);
+    if (resolved == NULL) {
+	FcPatternDestroy (pattern);
+	return CAIRO_TEST_NO_MEMORY;
+    }
+
+    if (FcPatternGetString (resolved, FC_FULLNAME, 0, &font_name) == FcResultMatch) {
+        if (strcmp((char*)font_name, FONT_FULLNAME) != 0) {
+            const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+            cairo_test_log (ctx, "Could not find %s font\n", FONT_FULLNAME);
+            return CAIRO_TEST_UNTESTED;
+        }
+    } else {
+        return CAIRO_TEST_FAILURE;
+    }
+
+    font_face = cairo_ft_font_face_create_for_pattern (resolved);
+    cairo_set_font_face (cr, font_face);
+
+    cairo_font_options_destroy (font_options);
+    cairo_font_face_destroy (font_face);
+    FcPatternDestroy (pattern);
+    FcPatternDestroy (resolved);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_font_options_t *font_options;
+    cairo_test_status_t result;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    result = set_color_emoji_font (cr);
+    if (result != CAIRO_TEST_SUCCESS)
+        return result;
+
+    cairo_set_font_size (cr, FONT_SIZE);
+
+    /* Color glyphs */
+    cairo_move_to (cr, MARGIN, FONT_SIZE + MARGIN);
+    cairo_show_text (cr, diamond_utf8);
+    cairo_show_text (cr, club_utf8);
+    cairo_show_text (cr, heart_utf8);
+    cairo_show_text (cr, spade_utf8);
+
+    /* Non-color glyphs */
+    font_options = cairo_font_options_create ();
+    cairo_font_options_set_color_mode (font_options, CAIRO_COLOR_MODE_NO_COLOR);
+    cairo_set_font_options (cr, font_options);
+    cairo_move_to (cr, MARGIN, FONT_SIZE*2 + MARGIN*2);
+    cairo_show_text (cr, diamond_utf8);
+    cairo_show_text (cr, club_utf8);
+    cairo_show_text (cr, heart_utf8);
+    cairo_show_text (cr, spade_utf8);
+
+    /* Color glyph text path */
+    cairo_font_options_set_color_mode (font_options, CAIRO_COLOR_MODE_COLOR);
+    cairo_set_font_options (cr, font_options);
+    cairo_font_options_destroy (font_options);
+    cairo_move_to (cr, MARGIN, FONT_SIZE*3 + MARGIN*3);
+    cairo_text_path (cr, diamond_utf8);
+    cairo_text_path (cr, club_utf8);
+    cairo_text_path (cr, heart_utf8);
+    cairo_text_path (cr, spade_utf8);
+    cairo_set_line_width (cr, 1);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (ft_svg_color_font,
+	    "Test color font",
+	    "svgrender", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw)
diff --git a/test/meson.build b/test/meson.build
index 47d590690..7d6d6e11b 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -425,6 +425,10 @@ test_ft_font_sources = [
   'ft-text-antialias-none.c',
 ]
 
+test_ft_svg_font_sources = [
+  'ft-svg-color-font.c',
+]
+
 test_gl_sources = [
   'gl-device-release.c',
   'gl-oversized-surface.c',
@@ -525,6 +529,9 @@ endif
 
 if feature_conf.get('CAIRO_HAS_FT_FONT', 0) == 1 and feature_conf.get('CAIRO_HAS_FC_FONT', 0) == 1
   test_sources += test_ft_font_sources
+  if conf.get('HAVE_FT_SVG_DOCUMENT', 0) == 1
+    test_sources += test_ft_svg_font_sources
+  endif
 endif
 
 if feature_conf.get('CAIRO_HAS_QUARTZ_SURFACE', 0) == 1
diff --git a/test/reference/ft-svg-color-font.ref.png b/test/reference/ft-svg-color-font.ref.png
new file mode 100644
index 000000000..22bafde03
Binary files /dev/null and b/test/reference/ft-svg-color-font.ref.png differ
commit 31700fed4f4eb2efb1b445c27b4016ea85bb14f3
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri May 6 21:14:13 2022 +0930

    Support SVG fonts in FT backend

diff --git a/configure.ac b/configure.ac
index c30ce0916..0420fba0c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -468,6 +468,8 @@ if test "x$use_ft" = "xyes"; then
 
   AC_CHECK_FUNCS(FT_Get_X11_Font_Format FT_GlyphSlot_Embolden FT_GlyphSlot_Oblique FT_Load_Sfnt_Table FT_Library_SetLcdFilter FT_Get_Var_Design_Coordinates FT_Done_MM_Var FT_Palette_Select)
 
+  AC_CHECK_TYPES([FT_SVG_Document], [], [], [[#include <freetype/otsvg.h>]])
+
   AC_MSG_CHECKING(for FT_HAS_COLOR)
   AC_LINK_IFELSE([AC_LANG_PROGRAM([
 #include <ft2build.h>
diff --git a/meson.build b/meson.build
index 756cfcd6c..831527c43 100644
--- a/meson.build
+++ b/meson.build
@@ -337,6 +337,9 @@ if freetype_dep.found()
     if not cc.links(files('meson-cc-tests/ft_has_color.c'), dependencies: freetype_dep, name: 'FT has color')
       conf.set('FT_HAS_COLOR', '(0)')
     endif
+    if cc.has_type('FT_SVG_Document', dependencies: freetype_dep, prefix: '#include <freetype/otsvg.h>')
+      conf.set('HAVE_FT_SVG_DOCUMENT', 1)
+    endif
     check_funcs += ft_check_funcs
     deps += [freetype_dep]
   endif
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 9328fca97..93b09bddd 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -285,7 +285,7 @@ cairo_svg_sources = cairo-svg-surface.c
 
 cairo_ft_headers = cairo-ft.h
 cairo_ft_private = cairo-ft-private.h
-cairo_ft_sources = cairo-ft-font.c
+cairo_ft_sources = cairo-ft-font.c cairo-svg-glyph-render.c
 
 # These are private, even though they look like public headers
 cairo_test_surfaces_private = \
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index b60a57510..c27800b00 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -46,6 +46,7 @@
 #include "cairo-ft-private.h"
 #include "cairo-pattern-private.h"
 #include "cairo-pixman-private.h"
+#include "cairo-recording-surface-private.h"
 
 #include <float.h>
 
@@ -67,6 +68,10 @@
 #include FT_LCD_FILTER_H
 #endif
 
+#if HAVE_FT_SVG_DOCUMENT
+#include FT_OTSVG_H
+#endif
+
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #elif !defined(access)
@@ -2539,7 +2544,7 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t     *scaled_font,
 	    FT_Palette_Set_Foreground_Color (face, color);
 	}
 
-	if (FT_Palette_Data_Get(face, &palette_data) == 0 && palette_data.num_palettes > 0) {
+	if (FT_Palette_Data_Get (face, &palette_data) == 0 && palette_data.num_palettes > 0) {
 	    FT_UShort palette_index = CAIRO_COLOR_PALETTE_DEFAULT;
 	    if (scaled_font->base.options.palette_index < palette_data.num_palettes)
 		palette_index = scaled_font->base.options.palette_index;
@@ -2621,45 +2626,216 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t     *scaled_font,
     return status;
 }
 
+#if HAVE_FT_SVG_DOCUMENT
 static cairo_int_status_t
-_cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
-				     cairo_scaled_glyph_t	*scaled_glyph,
-				     cairo_scaled_glyph_info_t	 info,
-				     FT_Face face,
-				     cairo_bool_t vertical_layout,
-				     int load_flags)
+_cairo_ft_scaled_glyph_init_record_svg_glyph (cairo_ft_scaled_font_t *scaled_font,
+					      cairo_scaled_glyph_t   *scaled_glyph,
+					      FT_Face                 face,
+					      cairo_text_extents_t   *extents)
 {
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
-    cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
-    FT_Glyph_Metrics *metrics;
-    double x_factor, y_factor;
-    FT_GlyphSlot glyph;
-    cairo_bool_t scaled_glyph_loaded = FALSE;
-    cairo_text_extents_t fs_metrics;
+    cairo_surface_t *recording_surface;
+    cairo_t *cr;
+    cairo_pattern_t *pattern;
+    FT_SVG_Document svg_doc = face->glyph->other;
+    char *svg_document;
+    FT_Color*        palette;
+    FT_Palette_Data  palette_data;
+
+    /* Create NULL terminated SVG document */
+    svg_document = strndup((const char*)svg_doc->svg_document, svg_doc->svg_document_length);
+
+    recording_surface =
+	cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+
+    cr = cairo_create (recording_surface);
+
+    if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) {
+        cairo_matrix_t scale;
+	scale = scaled_font->base.scale;
+	scale.x0 = scale.y0 = 0.;
+	cairo_set_matrix (cr, &scale);
+    }
 
-    cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
+    cairo_set_font_size (cr, 1.0);
+    cairo_set_font_options (cr, &scaled_font->base.options);
 
-    /* The font metrics for color glyphs should be the same as the
-     * outline glyphs. But just in case there aren't, request the
-     * color or outline metrics based on the font option and if
-     * the font has color.
-     */
-    int color_flag = 0;
-#ifdef FT_LOAD_COLOR
-    if (unscaled->have_color && scaled_font->base.options.color_mode != CAIRO_COLOR_MODE_NO_COLOR)
-	color_flag = FT_LOAD_COLOR;
+    pattern = cairo_pattern_create_rgb (0, 0, 0);
+    pattern->is_userfont_foreground = TRUE;
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    extents->x_bearing = DOUBLE_FROM_26_6(face->bbox.xMin);
+    extents->y_bearing = DOUBLE_FROM_26_6(face->bbox.yMin);
+    extents->width = DOUBLE_FROM_26_6(face->bbox.xMax) - extents->x_bearing;
+    extents->height = DOUBLE_FROM_26_6(face->bbox.yMax) - extents->y_bearing;
+
+    palette = NULL;
+    if (FT_Palette_Data_Get (face, &palette_data) == 0 && palette_data.num_palettes > 0) {
+	FT_UShort palette_index = CAIRO_COLOR_PALETTE_DEFAULT;
+	if (scaled_font->base.options.palette_index < palette_data.num_palettes)
+	    palette_index = scaled_font->base.options.palette_index;
+
+	if (FT_Palette_Select (face, palette_index, &palette) != 0)
+	    palette = NULL;
+    }
+
+    if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) {
+	status = _cairo_render_svg_glyph (svg_document,
+					  svg_doc->start_glyph_id,
+					  svg_doc->end_glyph_id,
+					  _cairo_scaled_glyph_index(scaled_glyph),
+					  svg_doc->units_per_EM,
+					  palette,
+					  palette ? palette_data.num_palette_entries : 0,
+					  cr);
+	if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
+	    status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+	if (status == CAIRO_STATUS_SUCCESS)
+	    status = cairo_status (cr);
+    }
+
+    cairo_destroy (cr);
+    free (svg_document);
+
+    if (status) {
+	cairo_surface_destroy (recording_surface);
+	scaled_glyph->color_glyph = FALSE;
+	scaled_glyph->color_glyph_set = TRUE;
+	return status;
+    }
+
+    _cairo_scaled_glyph_set_recording_surface (scaled_glyph,
+					       &scaled_font->base,
+					       recording_surface);
+
+    scaled_glyph->color_glyph = TRUE;
+    scaled_glyph->color_glyph_set = TRUE;
+
+    /* get metrics */
+
+    /* Copied from cairo-user-font.c */
+    cairo_matrix_t extent_scale;
+    double extent_x_scale;
+    double extent_y_scale;
+    double snap_x_scale;
+    double snap_y_scale;
+    double fixed_scale, x_scale, y_scale;
+
+    extent_scale = scaled_font->base.scale_inverse;
+    snap_x_scale = 1.0;
+    snap_y_scale = 1.0;
+    status = _cairo_matrix_compute_basis_scale_factors (&extent_scale,
+							&x_scale, &y_scale,
+							1);
+    if (status == CAIRO_STATUS_SUCCESS) {
+	if (x_scale == 0)
+	    x_scale = 1;
+	if (y_scale == 0)
+	    y_scale = 1;
+
+	snap_x_scale = x_scale;
+	snap_y_scale = y_scale;
+
+	/* since glyphs are pretty much 1.0x1.0, we can reduce error by
+	 * scaling to a larger square.  say, 1024.x1024. */
+	fixed_scale = 1024;
+	x_scale /= fixed_scale;
+	y_scale /= fixed_scale;
+
+	cairo_matrix_scale (&extent_scale, 1.0 / x_scale, 1.0 / y_scale);
+
+	extent_x_scale = x_scale;
+	extent_y_scale = y_scale;
+    }
+
+    {
+	/* compute width / height */
+	cairo_box_t bbox;
+	double x1, y1, x2, y2;
+	double x_scale, y_scale;
+
+	/* Compute extents.x/y/width/height from recording_surface,
+	 * in font space.
+	 */
+	status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
+						    &bbox,
+						    &extent_scale);
+	if (unlikely (status))
+	    return status;
+
+	_cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2);
+
+	x_scale = extent_x_scale;
+	y_scale = extent_y_scale;
+	extents->x_bearing = x1 * x_scale;
+	extents->y_bearing = y1 * y_scale;
+	extents->width     = (x2 - x1) * x_scale;
+	extents->height    = (y2 - y1) * y_scale;
+    }
+
+    if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) {
+	extents->x_advance = _cairo_lround (extents->x_advance / snap_x_scale) * snap_x_scale;
+	extents->y_advance = _cairo_lround (extents->y_advance / snap_y_scale) * snap_y_scale;
+    }
+
+    return status;
+}
 #endif
-    status = _cairo_ft_scaled_glyph_load_glyph (scaled_font,
-						scaled_glyph,
-						face,
-						load_flags | color_flag,
-						!hint_metrics,
-						vertical_layout);
-    if (unlikely (status))
+
+static cairo_int_status_t
+_cairo_ft_scaled_glyph_init_surface_svg_glyph (cairo_ft_scaled_font_t *scaled_font,
+					       cairo_scaled_glyph_t   *scaled_glyph,
+					       const cairo_color_t    *foreground_color)
+{
+    cairo_surface_t *surface;
+    int width, height;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    width = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x) -
+	_cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
+    height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) -
+	_cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+
+    cairo_surface_set_device_offset (surface,
+				     - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x),
+				     - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y));
+
+    status = _cairo_recording_surface_replay_with_foreground_color (scaled_glyph->recording_surface,
+								    surface,
+								    foreground_color);
+    if (unlikely (status)) {
+	cairo_surface_destroy(surface);
 	return status;
+    }
 
-    glyph = face->glyph;
-    scaled_glyph_loaded = hint_metrics;
+    _cairo_scaled_glyph_set_color_surface (scaled_glyph,
+					   &scaled_font->base,
+					   (cairo_image_surface_t *)surface,
+					   TRUE);
+    surface = NULL;
+
+    if (surface)
+	cairo_surface_destroy (surface);
+
+    return status;
+}
+
+static void
+_cairo_ft_scaled_glyph_get_metrics (cairo_ft_scaled_font_t     *scaled_font,
+				    FT_Face face,
+				    cairo_bool_t vertical_layout,
+				    int load_flags,
+				    cairo_text_extents_t *fs_metrics)
+{
+    FT_Glyph_Metrics *metrics;
+    double x_factor, y_factor;
+    cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+    cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
+    FT_GlyphSlot glyph = face->glyph;
 
     /*
      * Compute font-space metrics
@@ -2700,14 +2876,14 @@ _cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
 
 	    advance = ((metrics->horiAdvance + 32) & -64);
 
-	    fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
-	    fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
+	    fs_metrics->x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
+	    fs_metrics->y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
 
-	    fs_metrics.width  = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
-	    fs_metrics.height  = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
+	    fs_metrics->width  = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
+	    fs_metrics->height  = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
 
-	    fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor;
-	    fs_metrics.y_advance = 0;
+	    fs_metrics->x_advance = DOUBLE_FROM_26_6 (advance) * x_factor;
+	    fs_metrics->y_advance = 0;
 	} else {
 	    x1 = (metrics->vertBearingX) & -64;
 	    x2 = (metrics->vertBearingX + metrics->width + 63) & -64;
@@ -2716,39 +2892,142 @@ _cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
 
 	    advance = ((metrics->vertAdvance + 32) & -64);
 
-	    fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
-	    fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
+	    fs_metrics->x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
+	    fs_metrics->y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
 
-	    fs_metrics.width  = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
-	    fs_metrics.height  = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
+	    fs_metrics->width  = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
+	    fs_metrics->height  = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
 
-	    fs_metrics.x_advance = 0;
-	    fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor;
+	    fs_metrics->x_advance = 0;
+	    fs_metrics->y_advance = DOUBLE_FROM_26_6 (advance) * y_factor;
 	}
     } else {
-	fs_metrics.width  = DOUBLE_FROM_26_6 (metrics->width) * x_factor;
-	fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor;
+	fs_metrics->width  = DOUBLE_FROM_26_6 (metrics->width) * x_factor;
+	fs_metrics->height = DOUBLE_FROM_26_6 (metrics->height) * y_factor;
 
 	if (!vertical_layout) {
-	    fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor;
-	    fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor;
+	    fs_metrics->x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor;
+	    fs_metrics->y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor;
 
 	    if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE)
-		fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor;
+		fs_metrics->x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor;
 	    else
-		fs_metrics.x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor;
-	    fs_metrics.y_advance = 0 * y_factor;
+		fs_metrics->x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor;
+	    fs_metrics->y_advance = 0 * y_factor;
 	} else {
-	    fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor;
-	    fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor;
+	    fs_metrics->x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor;
+	    fs_metrics->y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor;
 
-	    fs_metrics.x_advance = 0 * x_factor;
+	    fs_metrics->x_advance = 0 * x_factor;
 	    if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE)
-		fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor;
+		fs_metrics->y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor;
 	    else
-		fs_metrics.y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor;
+		fs_metrics->y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor;
 	}
     }
+}
+
+static cairo_int_status_t
+_cairo_ft_scaled_glyph_init_metrics_svg_glyph (cairo_ft_scaled_font_t     *scaled_font,
+					       cairo_scaled_glyph_t	*scaled_glyph,
+					       FT_Face face,
+					       cairo_bool_t vertical_layout,
+					       int load_flags)
+{
+    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+#if HAVE_FT_SVG_DOCUMENT
+    cairo_text_extents_t fs_metrics;
+    cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
+
+    if (scaled_font->base.options.color_mode == CAIRO_COLOR_MODE_NO_COLOR)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_ft_scaled_glyph_load_glyph (scaled_font,
+						scaled_glyph,
+						face,
+						load_flags | FT_LOAD_COLOR,
+						!hint_metrics,
+						vertical_layout);
+    if (unlikely (status))
+	return status;
+
+    if (face->glyph->format != FT_GLYPH_FORMAT_SVG)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Get the advance. The other metrics are ignored */
+    _cairo_ft_scaled_glyph_get_metrics (scaled_font,
+					face,
+					vertical_layout,
+					load_flags,
+					&fs_metrics);
+
+    status = (cairo_int_status_t)_cairo_ft_scaled_glyph_init_record_svg_glyph (scaled_font,
+									       scaled_glyph,
+									       face,
+									       &fs_metrics);
+    if (status == CAIRO_INT_STATUS_SUCCESS) {
+	_cairo_scaled_glyph_set_metrics (scaled_glyph,
+					 &scaled_font->base,
+					 &fs_metrics);
+    }
+#endif
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
+				     cairo_scaled_glyph_t	*scaled_glyph,
+				     FT_Face face,
+				     cairo_bool_t vertical_layout,
+				     int load_flags)
+{
+    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+    cairo_text_extents_t fs_metrics;
+
+    cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
+
+    /* _cairo_ft_scaled_glyph_init_metrics() is called once the first
+     * time a cairo_scaled_glyph_t is created. We first check if this
+     * is an SVG glyph as SVG glyphs require the bounding box to be
+     * obtained from the ink extents of the SVG rendering.
+     *
+     * If an SVG glyph is found and succesfully rendered to a
+     * recording surface, the presence of
+     * CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE in the
+     * cairo_scaled_glyph_t indicates that this is an SVG glyph.
+     */
+    status = _cairo_ft_scaled_glyph_init_metrics_svg_glyph (scaled_font,
+							    scaled_glyph,
+							    face,
+							    vertical_layout,
+							    load_flags);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	return status;
+
+    /* The font metrics for color glyphs should be the same as the
+     * outline glyphs. But just in case there aren't, request the
+     * color or outline metrics based on the font option and if the
+     * font has color.
+     */
+    int color_flag = 0;
+#ifdef FT_LOAD_COLOR
+    if (scaled_font->unscaled->have_color && scaled_font->base.options.color_mode != CAIRO_COLOR_MODE_NO_COLOR)
+	color_flag = FT_LOAD_COLOR;
+#endif
+    status = _cairo_ft_scaled_glyph_load_glyph (scaled_font,
+						scaled_glyph,
+						face,
+						load_flags | color_flag,
+						!hint_metrics,
+						vertical_layout);
+    if (unlikely (status))
+	return status;
+
+    _cairo_ft_scaled_glyph_get_metrics (scaled_font,
+					face,
+					vertical_layout,
+					load_flags,
+					&fs_metrics);
 
     _cairo_scaled_glyph_set_metrics (scaled_glyph,
 				     &scaled_font->base,
@@ -2765,7 +3044,6 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
 {
     cairo_ft_scaled_font_t *scaled_font = abstract_font;
     cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
-    FT_GlyphSlot glyph;
     FT_Face face;
     int load_flags = scaled_font->ft_options.load_flags;
     cairo_bool_t vertical_layout = FALSE;
@@ -2797,7 +3075,6 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
     if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) {
 	status = _cairo_ft_scaled_glyph_init_metrics (scaled_font,
 						      scaled_glyph,
-						      info,
 						      face,
 						      vertical_layout,
 						      load_flags);
@@ -2806,15 +3083,23 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
     }
 
     if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) {
-	status = _cairo_ft_scaled_glyph_init_surface (scaled_font,
-						      scaled_glyph,
-						      CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE,
-						      face,
-						      foreground_color,
-						      vertical_layout,
-						      load_flags);
-	if (unlikely (status))
-	    goto FAIL;
+	if (scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE) {
+	    status = _cairo_ft_scaled_glyph_init_surface_svg_glyph (scaled_font,
+								    scaled_glyph,
+								    foreground_color);
+	    if (unlikely (status))
+		goto FAIL;
+	} else {
+	    status = _cairo_ft_scaled_glyph_init_surface (scaled_font,
+							  scaled_glyph,
+							  CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE,
+							  face,
+							  foreground_color,
+							  vertical_layout,
+							  load_flags);
+	    if (unlikely (status))
+		goto FAIL;
+	}
     }
 
     if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) {
@@ -2832,33 +3117,47 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
     if (info & CAIRO_SCALED_GLYPH_INFO_PATH) {
 	cairo_path_fixed_t *path = NULL; /* hide compiler warning */
 
-	/*
-	 * A kludge -- the above code will trash the outline,
-	 * so reload it. This will probably never occur though
-	 */
-	if ((info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) != 0) {
-	    scaled_glyph_loaded = FALSE;
-	    load_flags |= FT_LOAD_NO_BITMAP;
-	}
+	if (scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE) {
+	    path = _cairo_path_fixed_create ();
+	    if (!path) {
+		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		goto FAIL;
+	    }
 
-	if (!scaled_glyph_loaded) {
-	    status = _cairo_ft_scaled_glyph_load_glyph (scaled_font,
-							scaled_glyph,
-							face,
-							load_flags,
-							FALSE,
-							vertical_layout);
-	    if (unlikely (status))
+	    status = _cairo_recording_surface_get_path (scaled_glyph->recording_surface, path);
+	    if (unlikely (status)) {
+		_cairo_path_fixed_destroy (path);
 		goto FAIL;
+	    }
 
-	    glyph = face->glyph;
-	}
+	} else {
+	    /*
+	     * A kludge -- the above code will trash the outline,
+	     * so reload it. This will probably never occur though
+	     */
+	    if ((info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) != 0) {
+		scaled_glyph_loaded = FALSE;
+		load_flags |= FT_LOAD_NO_BITMAP;
+	    }
 
-	if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
-	    status = _decompose_glyph_outline (face, &scaled_font->ft_options.base,
-					       &path);
-	else
-	    status = CAIRO_INT_STATUS_UNSUPPORTED;
+	    if (!scaled_glyph_loaded) {
+		status = _cairo_ft_scaled_glyph_load_glyph (scaled_font,
+							    scaled_glyph,
+							    face,
+							    load_flags,
+							    FALSE,
+							    vertical_layout);
+		if (unlikely (status))
+		    goto FAIL;
+	    }
+
+	    if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
+		status = _decompose_glyph_outline (face, &scaled_font->ft_options.base,
+						   &path);
+	    } else {
+		status = CAIRO_INT_STATUS_UNSUPPORTED;
+	    }
+	}
 
 	if (unlikely (status))
 	    goto FAIL;
diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h
index 0dc811472..75d65b4db 100644
--- a/src/cairo-ft-private.h
+++ b/src/cairo-ft-private.h
@@ -55,6 +55,21 @@ _cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font);
 cairo_private unsigned int
 _cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font);
 
+#if HAVE_FT_SVG_DOCUMENT
+
+typedef struct FT_Color_ FT_Color;
+
+cairo_private cairo_status_t
+_cairo_render_svg_glyph (const char           *svg_document,
+                         unsigned long         first_glyph,
+                         unsigned long         last_glyph,
+                         unsigned long         glyph,
+                         double                units_per_em,
+                         FT_Color             *palette,
+                         int                   num_palette_entries,
+                         cairo_t              *cr);
+#endif
+
 CAIRO_END_DECLS
 
 #endif /* CAIRO_HAS_FT_FONT */
diff --git a/src/cairo-svg-glyph-render.c b/src/cairo-svg-glyph-render.c
new file mode 100644
index 000000000..738d4f8ff
--- /dev/null
+++ b/src/cairo-svg-glyph-render.c
@@ -0,0 +1,3225 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2022 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *	Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-ft-private.h"
+#include "cairo-array-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#if HAVE_FT_SVG_DOCUMENT
+
+#include <ft2build.h>
+#include FT_COLOR_H
+
+/* #define SVG_RENDER_PRINT_FUNCTIONS 1 */
+
+#define WHITE_SPACE_CHARS " \n\r\t\v\f"
+
+typedef struct {
+    const char *name;
+    int red;
+    int green;
+    int blue;
+} color_name_t;
+
+/* Must be sorted */
+static color_name_t color_names[] = {
+    { "aliceblue", 240, 248, 255 },
+    { "antiquewhite", 250, 235, 215 },
+    { "aqua",  0, 255, 255 },
+    { "aquamarine", 127, 255, 212 },
+    { "azure", 240, 255, 255 },
+    { "beige", 245, 245, 220 },
+    { "bisque", 255, 228, 196 },
+    { "black",  0, 0, 0 },
+    { "blanchedalmond", 255, 235, 205 },
+    { "blue",  0, 0, 255 },
+    { "blueviolet", 138, 43, 226 },
+    { "brown", 165, 42, 42 },
+    { "burlywood", 222, 184, 135 },
+    { "cadetblue",  95, 158, 160 },
+    { "chartreuse", 127, 255, 0 },
+    { "chocolate", 210, 105, 30 },
+    { "coral", 255, 127, 80 },
+    { "cornflowerblue", 100, 149, 237 },
+    { "cornsilk", 255, 248, 220 },
+    { "crimson", 220, 20, 60 },
+    { "cyan",  0, 255, 255 },
+    { "darkblue",  0, 0, 139 },
+    { "darkcyan",  0, 139, 139 },
+    { "darkgoldenrod", 184, 134, 11 },
+    { "darkgray", 169, 169, 169 },
+    { "darkgreen",  0, 100, 0 },
+    { "darkgrey", 169, 169, 169 },
+    { "darkkhaki", 189, 183, 107 },
+    { "darkmagenta", 139, 0, 139 },
+    { "darkolivegreen",  85, 107, 47 },
+    { "darkorange", 255, 140, 0 },
+    { "darkorchid", 153, 50, 204 },
+    { "darkred", 139, 0, 0 },
+    { "darksalmon", 233, 150, 122 },
+    { "darkseagreen", 143, 188, 143 },
+    { "darkslateblue",  72, 61, 139 },
+    { "darkslategray",  47, 79, 79 },
+    { "darkslategrey",  47, 79, 79 },
+    { "darkturquoise",  0, 206, 209 },
+    { "darkviolet", 148, 0, 211 },
+    { "deeppink", 255, 20, 147 },
+    { "deepskyblue",  0, 191, 255 },
+    { "dimgray", 105, 105, 105 },
+    { "dimgrey", 105, 105, 105 },
+    { "dodgerblue",  30, 144, 255 },
+    { "firebrick", 178, 34, 34 },
+    { "floralwhite", 255, 250, 240 },
+    { "forestgreen",  34, 139, 34 },
+    { "fuchsia", 255, 0, 255 },
+    { "gainsboro", 220, 220, 220 },
+    { "ghostwhite", 248, 248, 255 },
+    { "gold", 255, 215, 0 },
+    { "goldenrod", 218, 165, 32 },
+    { "gray", 128, 128, 128 },
+    { "green",  0, 128, 0 },
+    { "greenyellow", 173, 255, 47 },
+    { "grey", 128, 128, 128 },
+    { "honeydew", 240, 255, 240 },
+    { "hotpink", 255, 105, 180 },
+    { "indianred", 205, 92, 92 },
+    { "indigo",  75, 0, 130 },
+    { "ivory", 255, 255, 240 },
+    { "khaki", 240, 230, 140 },
+    { "lavender", 230, 230, 250 },
+    { "lavenderblush", 255, 240, 245 },
+    { "lawngreen", 124, 252, 0 },
+    { "lemonchiffon", 255, 250, 205 },
+    { "lightblue", 173, 216, 230 },
+    { "lightcoral", 240, 128, 128 },
+    { "lightcyan", 224, 255, 255 },
+    { "lightgoldenrodyellow", 250, 250, 210 },
+    { "lightgray", 211, 211, 211 },
+    { "lightgreen", 144, 238, 144 },
+    { "lightgrey", 211, 211, 211 },
+    { "lightpink", 255, 182, 193 },
+    { "lightsalmon", 255, 160, 122 },
+    { "lightseagreen",  32, 178, 170 },
+    { "lightskyblue", 135, 206, 250 },
+    { "lightslategray", 119, 136, 153 },
+    { "lightslategrey", 119, 136, 153 },
+    { "lightsteelblue", 176, 196, 222 },
+    { "lightyellow", 255, 255, 224 },
+    { "lime",  0, 255, 0 },
+    { "limegreen",  50, 205, 50 },
+    { "linen", 250, 240, 230 },
+    { "magenta", 255, 0, 255 },
+    { "maroon", 128, 0, 0 },
+    { "mediumaquamarine", 102, 205, 170 },
+    { "mediumblue",  0, 0, 205 },
+    { "mediumorchid", 186, 85, 211 },
+    { "mediumpurple", 147, 112, 219 },
+    { "mediumseagreen",  60, 179, 113 },
+    { "mediumslateblue", 123, 104, 238 },
+    { "mediumspringgreen",  0, 250, 154 },
+    { "mediumturquoise",  72, 209, 204 },
+    { "mediumvioletred", 199, 21, 133 },
+    { "midnightblue",  25, 25, 112 },
+    { "mintcream", 245, 255, 250 },
+    { "mistyrose", 255, 228, 225 },
+    { "moccasin", 255, 228, 181 },
+    { "navajowhite", 255, 222, 173 },
+    { "navy",  0, 0, 128 },
+    { "oldlace", 253, 245, 230 },
+    { "olive", 128, 128, 0 },
+    { "olivedrab", 107, 142, 35 },
+    { "orange", 255, 165, 0 },
+    { "orangered", 255, 69, 0 },
+    { "orchid", 218, 112, 214 },
+    { "palegoldenrod", 238, 232, 170 },
+    { "palegreen", 152, 251, 152 },
+    { "paleturquoise", 175, 238, 238 },
+    { "palevioletred", 219, 112, 147 },
+    { "papayawhip", 255, 239, 213 },
+    { "peachpuff", 255, 218, 185 },
+    { "peru", 205, 133, 63 },
+    { "pink", 255, 192, 203 },
+    { "plum", 221, 160, 221 },
+    { "powderblue", 176, 224, 230 },
+    { "purple", 128, 0, 128 },
+    { "red", 255, 0, 0 },
+    { "rosybrown", 188, 143, 143 },
+    { "royalblue",  65, 105, 225 },
+    { "saddlebrown", 139, 69, 19 },
+    { "salmon", 250, 128, 114 },
+    { "sandybrown", 244, 164, 96 },
+    { "seagreen",  46, 139, 87 },
+    { "seashell", 255, 245, 238 },
+    { "sienna", 160, 82, 45 },
+    { "silver", 192, 192, 192 },
+    { "skyblue", 135, 206, 235 },
+    { "slateblue", 106, 90, 205 },
+    { "slategray", 112, 128, 144 },
+    { "slategrey", 112, 128, 144 },
+    { "snow", 255, 250, 250 },
+    { "springgreen",  0, 255, 127 },
+    { "steelblue",  70, 130, 180 },
+    { "tan", 210, 180, 140 },
+    { "teal",  0, 128, 128 },
+    { "thistle", 216, 191, 216 },
+    { "tomato", 255, 99, 71 },
+    { "turquoise",  64, 224, 208 },
+    { "violet", 238, 130, 238 },
+    { "wheat", 245, 222, 179 },
+    { "white", 255, 255, 255 },
+    { "whitesmoke", 245, 245, 245 },
+    { "yellow", 255, 255, 0 },
+    { "yellowgreen", 154, 205, 50 }
+};
+
+typedef struct {
+    char *name;
+    char *value;
+} svg_attribute_t;
+
+typedef enum {
+    CONTAINER_ELEMENT,
+    EMPTY_ELEMENT,
+    PROCESSING_INSTRUCTION,
+    DOCTYPE,
+    CDATA,
+    COMMENT
+} tag_type_t;
+
+#define TOP_ELEMENT_TAG "_top"
+
+typedef struct _cairo_svg_element {
+    cairo_hash_entry_t base;
+    tag_type_t type;
+    char *tag;
+    char *id;
+    cairo_array_t attributes; /* svg_attribute_t */
+    cairo_array_t children; /* cairo_svg_element_t* */
+    cairo_array_t  content; /* char */
+    cairo_pattern_t *pattern; /* defined if a paint server */
+    struct _cairo_svg_element *next; /* next on element stack */
+} cairo_svg_element_t;
+
+typedef struct _cairo_svg_color {
+    enum { RGB, CURRENT_COLOR } type;
+    double red;
+    double green;
+    double blue;
+} cairo_svg_color_t;
+
+typedef struct _cairo_svg_paint {
+    enum { PAINT_COLOR, PAINT_SERVER, PAINT_NONE } type;
+    cairo_svg_color_t color;
+    cairo_svg_element_t *paint_server;
+} cairo_svg_paint_t;
+
+typedef enum {
+    GS_RENDER,
+    GS_NO_RENDER,
+    GS_COMPUTE_BBOX,
+    GS_CLIP
+} gs_mode_t;
+
+typedef struct _cairo_svg_graphics_state {
+    cairo_svg_paint_t fill;
+    cairo_svg_paint_t stroke;
+    cairo_svg_color_t color;
+    double fill_opacity;
+    double stroke_opacity;
+    double opacity;
+    cairo_fill_rule_t fill_rule;
+    cairo_fill_rule_t clip_rule;
+    cairo_path_t *clip_path;
+    char *dash_array;
+    double dash_offset;
+    gs_mode_t mode;
+    struct {
+        double x;
+        double y;
+        double width;
+        double height;
+    } bbox;
+    struct _cairo_svg_graphics_state *next;
+} cairo_svg_graphics_state_t;
+
+typedef enum {
+    BUILD_PATTERN_NONE,
+    BUILD_PATTERN_LINEAR,
+    BUILD_PATTERN_RADIAL
+} build_pattern_t;
+
+typedef struct _cairo_svg_glyph_render {
+    cairo_svg_element_t *tree;
+    cairo_hash_table_t *ids;
+    cairo_svg_graphics_state_t *graphics_state;
+    cairo_t *cr;
+    cairo_pattern_t *foreground_color;
+    double units_per_em;
+    struct {
+        cairo_svg_element_t *paint_server;
+        cairo_pattern_t *pattern;
+        build_pattern_t type;
+    } build_pattern;
+    int render_element_tree_depth;
+    int num_palette_entries;
+    FT_Color* palette;
+
+    /* Viewport */
+    double width;
+    double height;
+    cairo_bool_t view_port_set;
+
+    int debug; /* 0 = quiet, 1 = errors, 2 = warnings, 3 = info */
+} cairo_svg_glyph_render_t;
+
+
+#define ERROR 1
+#define WARNING 2
+#define INFO 3
+
+#define print_error(render, ...) cairo_svg_glyph_render_printf(render, ERROR, ##__VA_ARGS__)
+#define print_warning(render, ...) cairo_svg_glyph_render_printf(render, WARNING, ##__VA_ARGS__)
+#define print_info(render, ...) cairo_svg_glyph_render_printf(render, INFO, ##__VA_ARGS__)
+
+static void
+cairo_svg_glyph_render_printf (cairo_svg_glyph_render_t *svg_render,
+                               int level,
+                               const char *fmt, ...) CAIRO_PRINTF_FORMAT (3, 4);
+
+static void
+cairo_svg_glyph_render_printf (cairo_svg_glyph_render_t *svg_render,
+                               int level,
+                               const char *fmt, ...)
+{
+    va_list ap;
+
+    if (svg_render->debug >= level ) {
+        switch (level) {
+            case ERROR:
+                printf("ERROR: ");
+                break;
+            case WARNING:
+                printf("WARNING: ");
+                break;
+        }
+	va_start (ap, fmt);
+	vprintf (fmt, ap);
+	va_end (ap);
+	printf ("\n");
+    }
+}
+
+static cairo_bool_t
+string_equal (const char *s1, const char *s2)
+{
+    if (s1 && s2)
+        return strcmp (s1, s2) == 0;
+
+    if (!s1 && !s2)
+        return TRUE;
+
+    return FALSE;
+}
+
+static cairo_bool_t
+string_match (const char **p, const char *str)
+{
+    if (*p && strncmp (*p, str, strlen (str)) == 0) {
+        *p += strlen (str);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static const char *
+skip_space (const char *p)
+{
+    while (*p && _cairo_isspace (*p))
+        p++;
+
+    return p;
+}
+
+/* Skip over character c and and whitespace before or after. Returns
+ * NULL if c not found. */
+static const char *
+skip_char (const char *p, char c)
+{
+    while (_cairo_isspace (*p))
+        p++;
+
+    if (*p != c)
+        return NULL;
+
+    p++;
+
+    while (_cairo_isspace (*p))
+        p++;
+
+    return p;
+}
+
+static int
+_color_name_compare (const void *a, const void *b)
+{
+    const color_name_t *a_color = a;
+    const color_name_t *b_color = b;
+
+    return strcmp (a_color->name, b_color->name);
+}
+
+static void
+init_element_id_key (cairo_svg_element_t *element)
+{
+    element->base.hash = _cairo_hash_string (element->id);
+}
+
+static cairo_bool_t
+_element_id_equal (const void *key_a, const void *key_b)
+{
+    const cairo_svg_element_t *a = key_a;
+    const cairo_svg_element_t *b = key_b;
+
+    return string_equal (a->id, b->id);
+}
+
+/* Find element with the "id" attribute matching id. id may have the
+ * '#' prefix. It will be stripped before searching.
+ */
+static cairo_svg_element_t *
+lookup_element (cairo_svg_glyph_render_t *svg_render, const char *id)
+{
+    cairo_svg_element_t key;
+
+    if (!id || strlen (id) < 1)
+        return NULL;
+
+    key.id = (char *)(id[0] == '#' ? id + 1 : id);
+    init_element_id_key (&key);
+    return _cairo_hash_table_lookup (svg_render->ids, &key.base);
+}
+
+/* Find element with the "id" attribute matching url where url is of
+ * the form "url(#id)".
+ */
+static cairo_svg_element_t *
+lookup_url_element (cairo_svg_glyph_render_t *svg_render, const char *url)
+{
+    const char *p = url;
+    cairo_svg_element_t *element = NULL;
+
+    if (p && string_match (&p, "url")) {
+        p = skip_char (p, '(');
+        if (!p)
+            return NULL;
+
+        const char *end = strpbrk(p, WHITE_SPACE_CHARS ")");
+        if (end) {
+            char *id = strndup (p, end - p);
+            element = lookup_element (svg_render, id);
+            free (id);
+        }
+    }
+    return element;
+}
+
+static const char *
+get_attribute (const cairo_svg_element_t *element, const char *name)
+{
+    svg_attribute_t attr;
+    int num_elems, i;
+
+    num_elems = _cairo_array_num_elements (&element->attributes);
+    for (i = 0; i < num_elems; i++) {
+	_cairo_array_copy_element (&element->attributes, i, &attr);
+        if (string_equal (attr.name, name))
+            return attr.value;
+    }
+    return NULL;
+}
+
+static const char *
+get_href_attribute (const cairo_svg_element_t *element)
+{
+    svg_attribute_t attr;
+    int num_elems, i, len;
+
+    /* SVG2 requires the href attribute to be "href". Older versions
+     * used "xlink:href". I have seen at least one font that used an
+     * alternative name space eg "ns1:href". To keep things simple we
+     * search for an attribute named "href" or ending in ":href".
+     */
+    num_elems = _cairo_array_num_elements (&element->attributes);
+    for (i = 0; i < num_elems; i++) {
+	_cairo_array_copy_element (&element->attributes, i, &attr);
+        if (string_equal (attr.name, "href"))
+            return attr.value;
+
+        len = strlen (attr.name);
+        if (len > 4 && string_equal (attr.name + len - 5, ":href"))
+            return attr.value;
+    }
+    return NULL;
+}
+
+/* Get a float attribute or float percentage. If attribute is a
+ * percentage, the returned value is percentage * scale.  Does not
+ * modify value if it returns FALSE. This allows value to be set to a
+ * default before calling get_float_attribute(), then used without
+ * checking the return value of this function.
+ */
+static cairo_bool_t
+get_float_or_percent_attribute (const cairo_svg_element_t *element,
+                                const char *name,
+                                double scale,
+                                double *value)
+{
+    const char *p;
+    char *end;
+    double v;
+
+    p = get_attribute (element, name);
+    if (p) {
+        v = _cairo_strtod (p, &end);
+        if (end != p) {
+            *value = v;
+            if (*end == '%')
+                *value *= scale / 100.0;
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+/* Does not modify value if it returns FALSE. This allows value to be
+ * set to a default before calling get_float_attribute(), then used
+ * without checking the return value of this function.
+ */
+static cairo_bool_t
+get_float_attribute (const cairo_svg_element_t *element, const char *name, double *value)
+{
+    const char *p;
+    char *end;
+    double v;
+
+    p = get_attribute (element, name);
+    if (p) {
+        v = _cairo_strtod (p, &end);
+        if (end != p) {
+            *value = v;
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+static cairo_fill_rule_t
+get_fill_rule_attribute (const cairo_svg_element_t *element, const char *name, cairo_fill_rule_t default_value)
+{
+    const char *p;
+
+    p = get_attribute (element, name);
+    if (string_equal (p, "nonzero"))
+        return CAIRO_FILL_RULE_WINDING;
+    else if (string_equal (p, "evenodd"))
+        return CAIRO_FILL_RULE_EVEN_ODD;
+    else
+        return default_value;
+}
+
+static void
+free_elements (cairo_svg_glyph_render_t *svg_render,
+              cairo_svg_element_t      *element)
+{
+    int num_elems;
+
+    num_elems = _cairo_array_num_elements (&element->children);
+    for (int i = 0; i < num_elems; i++) {
+	cairo_svg_element_t *child;
+        _cairo_array_copy_element (&element->children, i, &child);
+	free_elements (svg_render, child);
+    }
+    _cairo_array_fini (&element->children);
+
+    num_elems = _cairo_array_num_elements (&element->attributes);
+    for (int i = 0; i < num_elems; i++) {
+	svg_attribute_t *attr = _cairo_array_index (&element->attributes, i);
+	free (attr->name);
+	free (attr->value);
+    }
+    _cairo_array_fini (&element->attributes);
+    _cairo_array_fini (&element->content);
+
+    free (element->tag);
+
+    if (element->id) {
+        _cairo_hash_table_remove (svg_render->ids, &element->base);
+        free (element->id);
+    }
+
+    if (element->pattern)
+        cairo_pattern_destroy (element->pattern);
+
+    free (element);
+}
+
+#if SVG_RENDER_PRINT_FUNCTIONS
+
+static void indent(int level)
+{
+    for (int i = 1; i < level; i++)
+        printf("  ");
+}
+
+static void
+print_element (cairo_svg_element_t *element, cairo_bool_t recurse, int level)
+{
+    char *content = strndup (_cairo_array_index_const (&element->content, 0),
+                             _cairo_array_num_elements (&element->content));
+
+    indent(level);
+    if (element->type == COMMENT) {
+        printf("<!--%s-->\n", content);
+    } else if (element->type == CDATA) {
+        printf("<![CDATA[%s]]>\n", content);
+    } else if (element->type == DOCTYPE) {
+        printf("<!DOCTYPE%s>\n", content);
+    } else if (element->type == PROCESSING_INSTRUCTION) {
+        printf("<?%s?>\n", content);
+    } else {
+        cairo_bool_t top_element = string_equal (element->tag, TOP_ELEMENT_TAG);
+
+        if (!top_element) {
+            printf("<%s", element->tag);
+            int num_elems = _cairo_array_num_elements (&element->attributes);
+            for (int i = 0; i < num_elems; i++) {
+                svg_attribute_t *attr = _cairo_array_index (&element->attributes, i);
+                printf(" %s=\"%s\"", attr->name, attr->value);
+            }
+            if (num_elems > 0)
+                printf(" ");
+
+            if (element->type == EMPTY_ELEMENT)
+                printf("/>\n");
+            else
+                printf(">\n");
+        }
+
+        if (element->type == CONTAINER_ELEMENT) {
+            if (recurse) {
+                int num_elems = _cairo_array_num_elements (&element->children);
+                for (int i = 0; i < num_elems; i++) {
+                    cairo_svg_element_t *child;
+                    _cairo_array_copy_element (&element->children, i, &child);
+                    print_element (child, TRUE, level + 1);
+                }
+            }
+            if (!top_element)
+                printf("</%s>\n", element->tag);
+        }
+    }
+    free (content);
+}
+#endif
+
+static const char *
+parse_list_of_floats (const char *p,
+                      int num_required,
+                      int num_optional,
+                      cairo_bool_t *have_optional,
+                      va_list ap)
+{
+    double d;
+    double *dp;
+    char *end;
+    const char *q = NULL;
+    int num_found = 0;
+
+    for (int i = 0; i < num_required + num_optional; i++) {
+        while (p && (*p == ',' || _cairo_isspace (*p)))
+            p++;
+
+        if (!p)
+            break;
+
+        d = _cairo_strtod (p, &end);
+        if (end == p) {
+            p = NULL;
+            break;
+        }
+        p = end;
+        dp = va_arg (ap, double *);
+        *dp = d;
+        num_found++;
+        if (num_found == num_required)
+            q = p;
+    }
+
+    if (num_optional > 0) {
+        if (num_found == num_required + num_optional) {
+            *have_optional = TRUE;
+        } else {
+            *have_optional = FALSE;
+            /* restore pointer to end of required floats */
+            p = q;
+        }
+    }
+
+    return p;
+}
+
+static const char *
+get_floats (const char *p,
+            int num_required,
+            int num_optional,
+            cairo_bool_t *have_optional,
+            ...)
+{
+    va_list ap;
+
+    va_start (ap, have_optional);
+    p = parse_list_of_floats (p, num_required, num_optional, have_optional, ap);
+    va_end (ap);
+    return p;
+}
+
+static const char *
+get_path_params (const char *p, int num_params, ...)
+{
+    va_list ap;
+
+    va_start (ap, num_params);
+    p = parse_list_of_floats (p, num_params, 0, NULL, ap);
+    va_end (ap);
+    return p;
+}
+
+static cairo_bool_t
+get_color (cairo_svg_glyph_render_t *svg_render,
+           const char               *s,
+           cairo_svg_color_t        *color)
+{
+    int len, matched;
+    unsigned r = 0, g = 0, b = 0;
+
+    if (!s)
+        return FALSE;
+
+    len = strlen(s);
+
+    if (string_equal (s, "inherit") ||
+        string_equal (s, "currentColor") ||
+        string_equal (s, "context-fill") ||
+        string_equal (s, "context-stroke"))
+    {
+        color->type = CURRENT_COLOR;
+        color->red = color->green = color->blue = 0;
+        return TRUE;
+    } else if (len > 0 && s[0] == '#') {
+        if (len == 4) {
+            matched = sscanf (s + 1, "%1x%1x%1x", &r, &g, &b);
+            if (matched == 3) {
+                /* Each digit is repeated to convert to 6 digits. eg 0x123 -> 0x112233 */
+                color->type = RGB;
+                color->red = 0x11*r/255.0;
+                color->green = 0x11*g/255.0;
+                color->blue = 0x11*b/255.0;
+                return TRUE;
+            }
+        } else if (len == 7) {
+            matched = sscanf (s + 1, "%2x%2x%2x", &r, &g, &b);
+            if (matched == 3) {
+                color->type = RGB;
+                color->red = r/255.0;
+                color->green = g/255.0;
+                color->blue = b/255.0;
+                return TRUE;
+            }
+        }
+    } else if (strncmp (s, "rgb", 3) == 0) {
+        matched = sscanf (s, "rgb ( %u , %u , %u )", &r, &g, &b);
+        if (matched == 3) {
+            color->type = RGB;
+            color->red = r/255.0;
+            color->green = g/255.0;
+            color->blue = b/255.0;
+            return TRUE;
+        }
+    } else if (strncmp (s, "var", 3) == 0) {
+        /* CPAL palettes colors. eg "var(--color0, yellow)" */
+        s += 3;
+        s = skip_char (s, '(');
+        if (!string_match (&s, "--color"))
+            return FALSE;
+
+        char *end;
+        int entry = strtol (s, &end, 10);
+        if (end == s)
+            return FALSE;
+
+        if (svg_render->palette && entry > 0 && entry < svg_render->num_palette_entries) {
+            FT_Color *palette_color = &svg_render->palette[entry];
+            color->type = RGB;
+            color->red = palette_color->red / 255.0;
+            color->green = palette_color->green/ 255.0;
+            color->blue = palette_color->blue / 255.0;
+            return TRUE;
+        } else {
+            /* Fallback color */
+            s = skip_char (end, ',');
+            if (!s)
+            return FALSE;
+
+            end = strpbrk(s, WHITE_SPACE_CHARS ")");
+            if (!end || end == s)
+            return FALSE;
+
+            char *fallback = strndup (s, end - s);
+            cairo_bool_t success = get_color (svg_render, fallback, color);
+            free (fallback);
+            return success;
+        }
+    } else {
+        const color_name_t *color_name;
+        color_name_t color_name_key;
+
+        color_name_key.name = (char *) s;
+        color_name = bsearch (&color_name_key,
+                              color_names,
+                              ARRAY_LENGTH (color_names),
+                              sizeof (color_name_t),
+                             _color_name_compare);
+        if (color_name) {
+            color->type = RGB;
+            color->red = color_name->red/255.0;
+            color->green = color_name->green/255.0;
+            color->blue = color_name->blue/255.0;
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+static void
+get_paint (cairo_svg_glyph_render_t *svg_render,
+           const char *p,
+           cairo_svg_paint_t *paint)
+{
+    cairo_svg_element_t *element;
+
+    if (string_match (&p, "none")) {
+        paint->type = PAINT_NONE;
+        paint->paint_server = NULL;
+    } else if (p && strncmp (p, "url", 3) == 0) {
+        element = lookup_url_element (svg_render, p);
+        if (element) {
+            paint->type = PAINT_SERVER;
+            paint->paint_server = element;
+        }
+    } else {
+        if (get_color (svg_render, p, &paint->color)) {
+            paint->type = PAINT_COLOR;
+            paint->paint_server = NULL;
+        }
+    }
+}
+
+#ifdef SVG_RENDER_PRINT_FUNCTIONS
+
+static void
+print_color (cairo_svg_color_t *color)
+{
+    switch (color->type) {
+        case FOREGROUND_COLOR:
+            printf("foreground");
+            break;
+        case RGB:
+            printf("#%02x%02x%02x",
+                   (int)(color->red*255),
+                   (int)(color->red*255),
+                   (int)(color->red*255));
+            break;
+    }
+}
+
+static void
+print_paint (cairo_svg_paint_t *paint)
+{
+    printf("Paint: ");
+    switch (paint->type) {
+        case PAINT_COLOR:
+            printf("color: ");
+            print_color (&paint->color);
+            break;
+        case PAINT_SERVER:
+            printf("server: %s", paint->paint_server->tag);
+            break;
+        case PAINT_NONE:
+            printf("none");
+            break;
+    }
+    printf("\n");
+}
+
+#endif
+
+static void
+parse_error (cairo_svg_glyph_render_t *svg_render,
+             const char *string,
+             const char *location,
+             const char *fmt,
+             ...) CAIRO_PRINTF_FORMAT (4, 5);
+
+static void
+parse_error (cairo_svg_glyph_render_t *svg_render,
+             const char *string,
+             const char *location,
+             const char *fmt,
+             ...)
+{
+    va_list ap;
+    const int context = 40;
+    const char *start;
+    const char *end;
+
+    if (svg_render->debug >= ERROR) {
+        printf("ERROR: ");
+	va_start (ap, fmt);
+	vprintf (fmt, ap);
+	va_end (ap);
+        putchar ('\n');
+        start = location - context;
+        if (start < string)
+            start = string;
+
+        end = location + strlen (location);
+        if (end - location > context)
+            end = location + context;
+
+        for (const char *p = start; p < end; p++) {
+            if (_cairo_isspace (*p))
+                putchar (' ');
+            else
+                putchar (*p);
+        }
+        putchar ('\n');
+
+        for (int i = 0; i < location - start; i++)
+            putchar(' ');
+        putchar ('^');
+        putchar ('\n');
+	printf (" at position %td\n", location - string);
+    }
+}
+
+static cairo_bool_t
+append_attribute (cairo_svg_element_t *element, svg_attribute_t *attribute)
+{
+    const char *p;
+    const char *end;
+    svg_attribute_t attr;
+
+    memset (&attr, 0, sizeof (attr));
+    if (string_equal (attribute->name, "style")) {
+        /* split style into individual attributes */
+        p = attribute->value;
+        while (*p) {
+            end = strchr (p, ':');
+            if (!end || end == p)
+                break;
+            attr.name = strndup (p, end - p);
+            p = end + 1;
+            p = skip_space(p);
+            end = strchr (p, ';');
+            if (!end)
+                end = strchr (p, 0);
+            if (end == p)
+                goto split_style_fail;
+
+            attr.value = strndup (p, end - p);
+            if (*end)
+                p = end + 1;
+
+            if (_cairo_array_append (&element->attributes, &attr))
+                goto split_style_fail;
+
+            memset (&attr, 0, sizeof (attr));
+            p = skip_space (p);
+        }
+    }
+
+    if (_cairo_array_append (&element->attributes, attribute))
+        return FALSE;
+
+    return TRUE;
+
+  split_style_fail:
+    free (attr.name);
+    free (attr.value);
+    return FALSE;
+}
+
+static cairo_bool_t
+add_child_element (cairo_svg_glyph_render_t *svg_render,
+                   cairo_svg_element_t *parent,
+                   cairo_svg_element_t *child)
+{
+    cairo_status_t status;
+    const char* id;
+
+    id = get_attribute (child, "id");
+    if (id) {
+        child->id = strdup (id);
+        init_element_id_key (child);
+	status = _cairo_hash_table_insert (svg_render->ids, &child->base);
+	if (unlikely (status))
+            return FALSE;
+    }
+
+    status = _cairo_array_append (&parent->children, &child);
+    return status == CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_svg_element_t *
+create_element (tag_type_t type, char *tag)
+{
+    cairo_svg_element_t *elem;
+    cairo_status_t status;
+
+    elem = _cairo_malloc (sizeof (cairo_svg_element_t));
+    if (unlikely (elem == NULL)) {
+	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        return NULL;
+    }
+
+    elem->type = type;
+    elem->tag = tag;
+    elem->id = NULL;
+    _cairo_array_init (&elem->attributes, sizeof(svg_attribute_t));
+    _cairo_array_init (&elem->children, sizeof(cairo_svg_element_t *));
+    _cairo_array_init (&elem->content, sizeof(char));
+    elem->pattern = NULL;
+    elem->next = NULL;
+
+    return elem;
+}
+
+static const char *
+parse_attributes (cairo_svg_glyph_render_t *svg_render,
+                  const char               *attributes,
+                  cairo_svg_element_t      *element)
+{
+    svg_attribute_t attr;
+    char quote_char;
+    const char *p;
+    const char *end;
+
+    p = attributes;
+    memset (&attr, 0, sizeof (svg_attribute_t));
+    p = skip_space (p);
+    while (*p && *p != '/' && *p != '>' && *p != '?') {
+        end = strpbrk(p, WHITE_SPACE_CHARS "=");
+        if (!end) {
+            parse_error (svg_render, attributes, p, "Could not find '='");
+            goto fail;
+        }
+
+        if (end == p) {
+            parse_error (svg_render, attributes, p, "Missing attribute name");
+            goto fail;
+        }
+
+        attr.name = strndup (p, end - p);
+        p = end;
+
+        p = skip_space (p);
+        if (*p != '=') {
+            parse_error (svg_render, attributes, p, "Expected '='");
+            goto fail;
+        }
+
+        p++;
+        p = skip_space (p);
+        if (*p == '\"' || *p == '\'') {
+            quote_char = *p;
+        } else {
+            parse_error (svg_render, attributes, p, "Could not find '\"' or '''");
+            goto fail;
+        }
+
+        p++;
+        end = strchr (p, quote_char);
+        if (!end) {
+            parse_error (svg_render, attributes, p, "Could not find '%c'", quote_char);
+            goto fail;
+        }
+
+        attr.value = strndup (p, end - p);
+        p = end + 1;
+
+        if (!append_attribute (element, &attr))
+            goto fail;
+
+        memset (&attr, 0, sizeof (svg_attribute_t));
+
+        p = skip_space (p);
+    }
+
+    return p;
+
+  fail:
+    free (attr.name);
+    free (attr.value);
+    return NULL;
+}
+
+static cairo_bool_t
+parse_svg (cairo_svg_glyph_render_t *svg_render,
+           const char               *svg_document)
+{
+    const char *p = svg_document;
+    const char *end;
+    int nesting; /* when > 0 we parse content */
+    cairo_svg_element_t *open_elem; /* Stack of open elements */
+    cairo_svg_element_t *new_elem = NULL;
+    char *name;
+    cairo_status_t status;
+
+    /* Create top level element to use as a container for all top
+     * level elements in the document and push it on the stack. */
+    open_elem = create_element (CONTAINER_ELEMENT, strdup(TOP_ELEMENT_TAG));
+
+    /* We don't want to add content to the top level container. There
+     * should only be whitesapce between tags. */
+    nesting = 0;
+
+    while (*p) {
+        if (nesting > 0) {
+            /* In an open element. Anything before the next '<' is content */
+            end = strchr (p, '<');
+            if (!end) {
+                parse_error (svg_render, svg_document, p, "Could not find '<'");
+                goto fail;
+            }
+            status = _cairo_array_append_multiple (&open_elem->content, p, end - p);
+            p = end;
+
+        } else {
+            p = skip_space (p);
+            if (*p == 0)
+                break; /* end of document */
+        }
+
+        /* We should now be at the start of a tag */
+        if (*p != '<') {
+            parse_error (svg_render, svg_document, p, "Could not find '<'");
+            goto fail;
+        }
+
+        p++;
+        if (*p == '!') {
+            p++;
+            if (string_match (&p, "[CDATA[")) {
+                new_elem = create_element (CDATA, NULL);
+                end = strstr (p, "]]>");
+                if (!end) {
+                    parse_error (svg_render, svg_document, p, "Could not find ']]>'");
+                    goto fail;
+                }
+
+                status = _cairo_array_append_multiple (&new_elem->content, p, end - p);
+                p = end + 3;
+            } else if (string_match (&p, "--")) {
+                new_elem = create_element (COMMENT, NULL);
+                end = strstr (p, "-->");
+                if (!end) {
+                    parse_error (svg_render, svg_document, p, "Could not find '-->'");
+                    goto fail;
+                }
+
+                status = _cairo_array_append_multiple (&new_elem->content, p, end - p);
+                p = end + 3;
+            } else if (string_match (&p, "DOCTYPE")) {
+                new_elem = create_element (DOCTYPE, NULL);
+                end = strchr (p, '>');
+                if (!end) {
+                    parse_error (svg_render, svg_document, p, "Could not find '>'");
+                    goto fail;
+                }
+
+                status = _cairo_array_append_multiple (&new_elem->content, p, end - p);
+                p = end + 1;
+            } else {
+                parse_error (svg_render, svg_document, p, "Invalid");
+                goto fail;
+            }
+
+            if (!add_child_element (svg_render, open_elem, new_elem))
+                goto fail;
+
+            new_elem = NULL;
+            continue;
+        }
+
+        if (*p == '?') {
+            p++;
+            new_elem = create_element (PROCESSING_INSTRUCTION, NULL);
+            end = strstr (p, "?>");
+            if (!end) {
+                parse_error (svg_render, svg_document, p, "Could not find '?>'");
+                goto fail;
+            }
+
+            status = _cairo_array_append_multiple (&new_elem->content, p, end - p);
+            p = end + 2;
+
+            if (!add_child_element (svg_render, open_elem, new_elem))
+                goto fail;
+
+            new_elem = NULL;
+            continue;
+        }
+
+        if (*p == '/') {
+            /* Closing tag */
+            p++;
+
+            /* find end of tag name */
+            end = strpbrk(p, WHITE_SPACE_CHARS ">");
+            if (!end) {
+                parse_error (svg_render, svg_document, p, "Could not find '>'");
+                goto fail;
+            }
+
+            name = strndup (p, end - p);
+            p = end;
+            p = skip_space (p);
+            if (*p != '>') {
+                parse_error (svg_render, svg_document, p, "Could not find '>'");
+                free (name);
+                goto fail;
+            }
+
+            p++;
+            if (nesting == 0) {
+                parse_error (svg_render, svg_document, p, "parse_elements: parsed </%s> but no matching start tag", name);
+                free (name);
+                goto fail;
+            }
+            if (!string_equal (name, open_elem->tag)) {
+                parse_error (svg_render, svg_document, p,
+                             "parse_elements: found </%s> but current open tag is <%s>",
+                             name, open_elem->tag);
+                free (name);
+                goto fail;
+            }
+
+            /* pop top element on open elements stack into new_elem */
+            new_elem = open_elem;
+            open_elem = open_elem->next;
+            new_elem->next = NULL;
+            nesting--;
+
+            free (name);
+            if (!add_child_element (svg_render, open_elem, new_elem))
+                goto fail;
+
+            new_elem = NULL;
+            continue;
+        }
+
+        /* We should now be in a start or empty element tag */
+
+        /* find end of tag name */
+        end = strpbrk(p, WHITE_SPACE_CHARS ">");
+        if (!end) {
+            parse_error (svg_render, svg_document, p, "Could not find '>'");
+            goto fail;
+        }
+
+        name = strndup (p, end - p);
+        p = end;
+
+        new_elem = create_element (CONTAINER_ELEMENT, name);
+        p = parse_attributes (svg_render, p, new_elem);
+        if (!p)
+            goto fail;
+
+        p = skip_space (p);
+        if (*p == '/') {
+            new_elem->type = EMPTY_ELEMENT;
+            p++;
+        }
+
+        if (!p || *p != '>') {
+            print_error (svg_render, "Could not find '>'");
+            goto fail;
+        }
+
+        p++;
+        if (new_elem->type == EMPTY_ELEMENT) {
+            if (!add_child_element (svg_render, open_elem, new_elem))
+                goto fail;
+
+            new_elem = NULL;
+        } else {
+            /* push new elem onto open elements stack */
+            new_elem->next = open_elem;
+            open_elem = new_elem;
+            new_elem = NULL;
+            nesting++;
+        }
+    }
+
+    if (nesting != 0) {
+        parse_error (svg_render, svg_document, p, "Missing closing tag for <%s>", open_elem->tag);
+        goto fail;
+    }
+
+    svg_render->tree = open_elem;
+    return TRUE;
+
+  fail:
+    if (new_elem)
+        free_elements (svg_render, new_elem);
+
+    while (open_elem) {
+        cairo_svg_element_t *elem = open_elem;
+        open_elem = open_elem->next;
+        free_elements (svg_render, elem);
+    }
+
+    return FALSE;
+}
+
+static cairo_bool_t
+parse_transform (const char *p, cairo_matrix_t *matrix)
+{
+    cairo_matrix_t m;
+    double x, y, a;
+    cairo_bool_t have_optional;
+
+    cairo_matrix_init_identity (matrix);
+    while (p) {
+        while (p && (*p == ',' || _cairo_isspace (*p)))
+            p++;
+
+        if (!p || *p == 0)
+            break;
+
+        if (string_match (&p, "matrix")) {
+            p = skip_char (p, '(');
+            if (!p)
+                break;
+
+            p = get_floats (p, 6, 0, NULL, &m.xx, &m.yx, &m.xy, &m.yy, &m.x0, &m.y0);
+            if (!p)
+                break;
+
+            p = skip_char (p, ')');
+            if (!p)
+                break;
+
+            cairo_matrix_multiply (matrix, &m, matrix);
+
+        } else if (string_match (&p, "translate")) {
+            p = skip_char (p, '(');
+            if (!p)
+                break;
+
+            p = get_floats (p, 1, 1, &have_optional, &x, &y);
+            if (!p)
+                break;
+
+            p = skip_char (p, ')');
+            if (!p)
+                break;
+
+            if (!have_optional)
+                y = 0;
+
+            cairo_matrix_translate (matrix, x, y);
+
+        } else if (string_match (&p, "scale")) {
+            p = skip_char (p, '(');
+            if (!p)
+                break;
+
+            p = get_floats (p, 1, 1, &have_optional, &x, &y);
+            if (!p)
+                break;
+
+            p = skip_char (p, ')');
+            if (!p)
+                break;
+
+            if (!have_optional)
+                y = x;
+
+            cairo_matrix_scale (matrix, x, y);
+
+        } else if (string_match (&p, "rotate")) {
+            p = skip_char (p, '(');
+            if (!p)
+                break;
+
+            p = get_floats (p, 1, 2, &have_optional, &a, &x, &y);
+            if (!p)
+                break;
+
+            p = skip_char (p, ')');
+            if (!p)
+                break;
+
+            if (!have_optional) {
+                x = 0;
+                y = 0;
+            }
+
+            a *= M_PI/180.0;
+            cairo_matrix_translate (matrix, x, y);
+            cairo_matrix_rotate (matrix, a);
+            cairo_matrix_translate (matrix, -x, -y);
+
+        } else if (string_match (&p, "skewX")) {
+            p = skip_char (p, '(');
+            if (!p)
+                break;
+
+            p = get_floats (p, 1, 0, NULL, &a);
+            if (!p)
+                break;
+
+            p = skip_char (p, ')');
+            if (!p)
+                break;
+
+            a *= M_PI/180.0;
+            cairo_matrix_init_identity (&m);
+            m.xy = tan (a);
+            cairo_matrix_multiply (matrix, &m, matrix);
+
+        } else if (string_match (&p, "skewY")) {
+            p = skip_char (p, '(');
+            if (!p)
+                break;
+
+            p = get_floats (p, 1, 0, NULL, &a);
+            if (!p)
+                break;
+
+            p = skip_char (p, ')');
+            if (!p)
+                break;
+
+            a *= M_PI/180.0;
+            cairo_matrix_init_identity (&m);
+            m.yx = tan (a);
+            cairo_matrix_multiply (matrix, &m, matrix);
+
+        } else {
+            break;
+        }
+    }
+    return p != NULL;
+}
+
+static void
+render_element_tree (cairo_svg_glyph_render_t *svg_render,
+                     cairo_svg_element_t      *element,
+                     cairo_svg_element_t      *display_element,
+                     cairo_bool_t              children_only);
+
+static cairo_pattern_t *
+create_pattern (cairo_svg_glyph_render_t *svg_render,
+                cairo_svg_element_t      *paint_server)
+{
+    cairo_pattern_t *pattern = NULL;
+
+    if (paint_server) {
+        svg_render->build_pattern.paint_server = paint_server;
+        render_element_tree (svg_render, paint_server, NULL, FALSE);
+        pattern = svg_render->build_pattern.pattern;
+        svg_render->build_pattern.pattern = NULL;
+        svg_render->build_pattern.paint_server = NULL;
+        svg_render->build_pattern.type = BUILD_PATTERN_NONE;
+    }
+
+    if (!pattern)
+        pattern = cairo_pattern_create_rgb (0, 0, 0);
+
+    return pattern;
+}
+
+static cairo_bool_t
+render_element_svg (cairo_svg_glyph_render_t *svg_render,
+                    cairo_svg_element_t      *element,
+                    cairo_bool_t              end_tag)
+{
+    double width, height;
+    double vb_x, vb_y, vb_height, vb_width;
+    const char *p;
+    const char *end;
+
+    if (end_tag)
+        return FALSE;
+
+    /* Default viewport width, height is EM square */
+    if (!get_float_or_percent_attribute (element, "width", svg_render->units_per_em, &width))
+        width = svg_render->units_per_em;
+
+    if (!get_float_or_percent_attribute (element, "height", svg_render->units_per_em, &height))
+        height = svg_render->units_per_em;
+
+    /* Transform viewport to unit square, centering it if width != height. */
+    if (width > height) {
+        cairo_scale (svg_render->cr, 1.0/width, 1.0/width);
+        cairo_translate (svg_render->cr, 0, (width - height)/2.0);
+    } else {
+        cairo_scale (svg_render->cr, 1.0/height, 1.0/height);
+        cairo_translate (svg_render->cr, (height - width)/2.0, 0);
+    }
+
+    svg_render->width = width;
+    svg_render->height = height;
+
+    p = get_attribute (element, "viewBox");
+    if (p) {
+        /* Transform viewport to viewbox */
+        end = get_path_params (p, 4, &vb_x, &vb_y, &vb_width, &vb_height);
+        if (!end) {
+            print_warning (svg_render, "viewBox expected 4 numbers: %s", p);
+            return FALSE;
+        }
+        cairo_translate (svg_render->cr, -vb_x * width/vb_width, -vb_y * width/vb_width);
+        cairo_scale (svg_render->cr, width/vb_width, height/vb_height);
+        svg_render->width = vb_width;
+        svg_render->height = vb_height;
+    }
+
+    svg_render->view_port_set = TRUE;
+    return TRUE;
+}
+
+static cairo_bool_t
+render_element_clip_path (cairo_svg_glyph_render_t *svg_render,
+                          cairo_svg_element_t      *element,
+                          cairo_bool_t              end_tag)
+{
+    cairo_svg_graphics_state_t *gs = svg_render->graphics_state;
+    const char *p;
+
+    if (end_tag || gs->mode != GS_CLIP || svg_render->build_pattern.type != BUILD_PATTERN_NONE) {
+        return FALSE;
+    }
+
+    p = get_attribute (element, "clipPathUnits");
+    if (string_equal (p, "objectBoundingBox")) {
+        cairo_translate (svg_render->cr,
+                                svg_render->graphics_state->bbox.x,
+                                svg_render->graphics_state->bbox.y);
+        cairo_scale (svg_render->cr,
+                     svg_render->graphics_state->bbox.width,
+                     svg_render->graphics_state->bbox.height);
+    }
+
+    return TRUE;
+}
+
+static void
+apply_gradient_attributes (cairo_svg_glyph_render_t *svg_render,
+                           cairo_svg_element_t      *element)
+{
+    cairo_pattern_t *pattern = svg_render->build_pattern.pattern;
+    cairo_bool_t object_bbox = TRUE;
+    cairo_matrix_t transform;
+    cairo_matrix_t mat;
+    const char *p;
+
+    if (!pattern)
+        return;
+
+    p = get_attribute (element, "gradientUnits");
+    if (string_equal (p, "userSpaceOnUse"))
+        object_bbox = FALSE;
+
+    cairo_matrix_init_identity (&mat);
+    if (object_bbox) {
+        cairo_matrix_translate (&mat,
+                                svg_render->graphics_state->bbox.x,
+                                svg_render->graphics_state->bbox.y);
+        cairo_matrix_scale (&mat,
+                            svg_render->graphics_state->bbox.width,
+                            svg_render->graphics_state->bbox.height);
+    }
+
+    p = get_attribute (element, "gradientTransform");
+     if (parse_transform (p, &transform))
+         cairo_matrix_multiply (&mat, &transform, &mat);
+
+    if (cairo_matrix_invert (&mat) == CAIRO_STATUS_SUCCESS)
+        cairo_pattern_set_matrix (pattern, &mat);
+
+    p = get_attribute (element, "spreadMethod");
+    if (string_equal (p, "reflect"))
+        cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
+    else if (string_equal (p, "repeat"))
+        cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+}
+
+static cairo_bool_t
+render_element_linear_gradient (cairo_svg_glyph_render_t *svg_render,
+                                cairo_svg_element_t      *element,
+                                cairo_bool_t              end_tag)
+{
+    double x1, y1, x2, y2;
+
+    if (svg_render->build_pattern.paint_server != element ||
+        end_tag ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    /* FIXME default value for userSpaceOnUse? */
+    double width = 1.0;
+    double height = 1.0;
+
+    if (!get_float_or_percent_attribute (element, "x1", width, &x1))
+        x1 = 0.0;
+
+    if (!get_float_or_percent_attribute (element, "y1", height, &y1))
+        y1 = 0.0;
+
+    if (!get_float_or_percent_attribute (element, "x2", width, &x2))
+        x2 = width;
+
+    if (!get_float_or_percent_attribute (element, "y2", height, &y2))
+        y2 = 0.0;
+
+    if (svg_render->build_pattern.pattern)
+        abort();
+
+    svg_render->build_pattern.pattern = cairo_pattern_create_linear (x1, y1, x2, y2);
+    svg_render->build_pattern.type = BUILD_PATTERN_LINEAR;
+    apply_gradient_attributes (svg_render, element);
+    return TRUE;
+}
+
+static cairo_bool_t
+render_element_radial_gradient (cairo_svg_glyph_render_t *svg_render,
+                                cairo_svg_element_t      *element,
+                                cairo_bool_t              end_tag)
+{
+    double cx, cy, r, fx, fy;
+
+    if (svg_render->build_pattern.paint_server != element ||
+        end_tag ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    /* FIXME default value for userSpaceOnUse? */
+    double width = 1.0;
+    double height = 1.0;
+
+    if (!get_float_or_percent_attribute (element, "cx", width, &cx))
+        cx = 0.5 * width;
+
+    if (!get_float_or_percent_attribute (element, "cy", height, &cy))
+        cy = 0.5 * height;
+
+    if (!get_float_or_percent_attribute (element, "r", width, &r))
+        r = 0.5 * width;
+
+    if (!get_float_or_percent_attribute (element, "fx", width, &fx))
+        fx = cx;
+
+    if (!get_float_or_percent_attribute (element, "fy", height, &fy))
+        fy = cy;
+
+    svg_render->build_pattern.pattern = cairo_pattern_create_radial (fx, fy, 0, cx, cy, r);
+    svg_render->build_pattern.type = BUILD_PATTERN_RADIAL;
+    apply_gradient_attributes (svg_render, element);
+    return TRUE;
+}
+
+static cairo_bool_t
+render_element_stop (cairo_svg_glyph_render_t *svg_render,
+                     cairo_svg_element_t      *element,
+                     cairo_bool_t              end_tag)
+{
+    double offset, opacity;
+    cairo_pattern_t *pattern = svg_render->build_pattern.pattern;
+
+    if (!pattern)
+        return FALSE;
+
+    if (cairo_pattern_get_type (pattern) != CAIRO_PATTERN_TYPE_LINEAR &&
+        cairo_pattern_get_type (pattern) != CAIRO_PATTERN_TYPE_RADIAL)
+        return FALSE;
+
+    if (!get_float_or_percent_attribute (element, "offset", 1.0, &offset))
+        return FALSE;
+
+    if (!get_float_attribute (element, "stop-opacity", &opacity))
+        opacity = 1.0;
+
+    cairo_svg_color_t color;
+    get_color (svg_render, "black", &color);
+    get_color (svg_render, get_attribute(element, "stop-color"), &color);
+    if (color.type == RGB) {
+        cairo_pattern_add_color_stop_rgba (pattern,
+                                           offset,
+                                           color.red,
+                                           color.green,
+                                           color.blue,
+                                           opacity);
+    }
+    return TRUE;
+}
+
+static cairo_bool_t
+render_element_g (cairo_svg_glyph_render_t *svg_render,
+                  cairo_svg_element_t      *element,
+                  cairo_bool_t              end_tag)
+{
+    if (svg_render->graphics_state->mode == GS_NO_RENDER ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    if (!end_tag) {
+        cairo_push_group (svg_render->cr);
+    } else {
+        cairo_pop_group_to_source (svg_render->cr);
+        cairo_paint_with_alpha (svg_render->cr, svg_render->graphics_state->opacity);
+    }
+    return TRUE;
+}
+
+typedef struct {
+    const char *data; /* current position in base64 data */
+    char buf[3]; /* decode buffer */
+    int buf_pos; /* current position in buf_pos. */
+} base64_decode_t;
+
+static cairo_status_t
+_read_png_from_base64 (void *closure, unsigned char *data, unsigned int length)
+{
+    base64_decode_t *decode = closure;
+    int n, c;
+    unsigned val;
+
+    while (length) {
+        if (decode->buf_pos >= 0) {
+            *data++ = decode->buf[decode->buf_pos++];
+            length--;
+            if (decode->buf_pos == 3)
+                decode->buf_pos = -1;
+        }
+        if (length > 0 && decode->buf_pos < 0) {
+            n = 0;
+            while (*decode->data && n < 4) {
+                c = *decode->data++;
+                if (c >='A' && c <='Z') {
+                    val = (val << 6) | (c -'A');
+                    n++;
+                } else if (c >='a' && c <='z') {
+                    val = (val << 6) | (c -'a' + 26);
+                    n++;
+                } else if (c >='0' && c <='9') {
+                    val = (val << 6) | (c -'0' + 52);
+                    n++;
+                } else if (c =='+') {
+                    val = (val << 6) | 62;
+                    n++;
+                } else if (c =='/') {
+                    val = (val << 6) | 63;
+                    n++;
+                } else if (c == '=') {
+                    val = (val << 6);
+                    n++;
+                }
+            }
+            if (n < 4)
+                return CAIRO_STATUS_READ_ERROR;
+
+            decode->buf[0] = val >> 16;
+            decode->buf[1] = val >> 8;
+            decode->buf[2] = val >> 0;
+            decode->buf_pos = 0;
+        }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+render_element_image (cairo_svg_glyph_render_t *svg_render,
+                      cairo_svg_element_t      *element,
+                      cairo_bool_t              end_tag)
+{
+    double x, y, width, height;
+    int w, h;
+    const char *data;
+    cairo_surface_t *surface;
+    base64_decode_t decode;
+
+    if (svg_render->graphics_state->mode == GS_NO_RENDER ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    if (!get_float_attribute (element, "x", &x))
+        x = 0;
+
+    if (!get_float_attribute (element, "y", &y))
+        y = 0;
+
+    if (!get_float_attribute (element, "width", &width))
+        return FALSE;
+
+    if (!get_float_attribute (element, "height", &height))
+        return FALSE;
+
+    data = get_href_attribute (element);
+    if (!data)
+        return FALSE;
+
+    if (!string_match (&data, "data:image/png;base64,"))
+        return FALSE;
+
+    decode.data = data;
+    decode.buf_pos = -1;
+    surface = cairo_image_surface_create_from_png_stream (_read_png_from_base64, &decode);
+    if (cairo_surface_status (surface)) {
+        print_warning (svg_render, "Unable to decode PNG");
+        cairo_surface_destroy (surface);
+        return FALSE;
+    }
+
+    w = cairo_image_surface_get_width (surface);
+    h = cairo_image_surface_get_height (surface);
+
+    if (w > 0 && h > 0) {
+        cairo_translate (svg_render->cr, x, y);
+        cairo_scale (svg_render->cr, width/w, height/h);
+        cairo_set_source_surface (svg_render->cr, surface, 0, 0);
+        cairo_paint (svg_render->cr);
+    }
+
+    cairo_surface_destroy (surface);
+
+    return FALSE;
+}
+
+static cairo_bool_t
+render_element_use (cairo_svg_glyph_render_t *svg_render,
+                    cairo_svg_element_t      *element,
+                    cairo_bool_t              end_tag)
+{
+    double x = 0;
+    double y = 0;
+    const char *id;
+
+    if (end_tag || svg_render->graphics_state->mode == GS_NO_RENDER ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    get_float_attribute (element, "x", &x);
+    get_float_attribute (element, "y", &y);
+
+    id = get_href_attribute (element);
+    if (!id)
+        return FALSE;
+
+    cairo_svg_element_t *use_element = lookup_element (svg_render, id);
+    cairo_translate (svg_render->cr, x, y);
+    render_element_tree (svg_render, use_element, NULL, FALSE);
+    return TRUE;
+}
+
+static cairo_bool_t
+draw_path (cairo_svg_glyph_render_t *svg_render)
+{
+    cairo_svg_graphics_state_t *gs = svg_render->graphics_state;
+    cairo_pattern_t *pattern;
+    cairo_bool_t opacity_group = FALSE;
+
+    if (gs->mode == GS_COMPUTE_BBOX) {
+        cairo_set_source_rgb (svg_render->cr, 0, 0, 0);
+        cairo_set_fill_rule (svg_render->cr, gs->fill_rule);
+        cairo_fill (svg_render->cr);
+        return FALSE;
+    } else if (gs->mode == GS_CLIP) {
+        return FALSE;
+    }
+
+    if (gs->opacity < 1.0) {
+        cairo_push_group (svg_render->cr);
+        opacity_group = TRUE;
+    }
+
+    cairo_path_t *path = cairo_copy_path (svg_render->cr);
+    cairo_new_path (svg_render->cr);
+
+    if (gs->fill.type != PAINT_NONE) {
+        cairo_bool_t group = FALSE;
+        if (gs->fill.type == PAINT_COLOR) {
+            if (gs->fill.color.type == RGB) {
+                cairo_set_source_rgba (svg_render->cr,
+                                       gs->fill.color.red,
+                                       gs->fill.color.green,
+                                       gs->fill.color.blue,
+                                       gs->fill_opacity);
+            } else if (gs->fill.color.type == CURRENT_COLOR) {
+                cairo_set_source_rgba (svg_render->cr,
+                                       gs->color.red,
+                                       gs->color.green,
+                                       gs->color.blue,
+                                       gs->fill_opacity);
+            }
+        } else if (gs->fill.type == PAINT_SERVER) {
+            pattern = create_pattern (svg_render, gs->fill.paint_server);
+            cairo_set_source (svg_render->cr, pattern);
+            cairo_pattern_destroy (pattern);
+            if (gs->fill_opacity < 1.0)
+                group = TRUE;
+        }
+
+        if (group)
+            cairo_push_group (svg_render->cr);
+
+        cairo_append_path (svg_render->cr, path);
+        cairo_set_fill_rule (svg_render->cr, gs->fill_rule);
+        cairo_fill (svg_render->cr);
+        if (group) {
+            cairo_pop_group_to_source (svg_render->cr);
+            cairo_paint_with_alpha (svg_render->cr, gs->fill_opacity);
+        }
+    }
+
+    if (gs->stroke.type != PAINT_NONE) {
+        cairo_bool_t group = FALSE;
+        if (gs->stroke.type == PAINT_COLOR) {
+            if (gs->stroke.color.type == RGB) {
+                cairo_set_source_rgba (svg_render->cr,
+                                       gs->stroke.color.red,
+                                       gs->stroke.color.green,
+                                       gs->stroke.color.blue,
+                                       gs->stroke_opacity);
+            } else if (gs->stroke.color.type == CURRENT_COLOR) {
+                cairo_set_source_rgba (svg_render->cr,
+                                       gs->color.red,
+                                       gs->color.green,
+                                       gs->color.blue,
+                                       gs->stroke_opacity);
+            }
+        } else if (gs->stroke.type == PAINT_SERVER) {
+            pattern = create_pattern (svg_render, gs->stroke.paint_server);
+            cairo_set_source (svg_render->cr, pattern);
+            cairo_pattern_destroy (pattern);
+            if (gs->stroke_opacity < 1.0)
+                group = TRUE;
+        }
+
+        if (group)
+            cairo_push_group (svg_render->cr);
+
+        cairo_append_path (svg_render->cr, path);
+        cairo_stroke (svg_render->cr);
+
+        if (group) {
+            cairo_pop_group_to_source (svg_render->cr);
+            cairo_paint_with_alpha (svg_render->cr, gs->stroke_opacity);
+        }
+    }
+
+    cairo_path_destroy (path);
+
+    if (opacity_group) {
+        cairo_pop_group_to_source (svg_render->cr);
+        cairo_paint_with_alpha (svg_render->cr, gs->opacity);
+    }
+    return TRUE;
+}
+
+static void
+elliptical_arc (cairo_svg_glyph_render_t *svg_render,
+                double                    cx,
+                double                    cy,
+                double                    rx,
+                double                    ry,
+                double                    angle1,
+                double                    angle2)
+{
+    cairo_save (svg_render->cr);
+    cairo_translate (svg_render->cr, cx, cy);
+    cairo_scale (svg_render->cr, rx, ry);
+    cairo_arc (svg_render->cr, 0, 0, 1, angle1, angle2);
+    cairo_restore (svg_render->cr);
+}
+
+static cairo_bool_t
+render_element_rect (cairo_svg_glyph_render_t *svg_render,
+                     cairo_svg_element_t      *element,
+                     cairo_bool_t              end_tag)
+{
+    double x = 0;
+    double y = 0;
+    double width = svg_render->width;
+    double height = svg_render->height;
+    double rx = 0;
+    double ry = 0;
+
+    if (end_tag ||
+        svg_render->graphics_state->mode == GS_NO_RENDER ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    get_float_or_percent_attribute (element, "x", svg_render->width, &x);
+    get_float_or_percent_attribute (element, "y", svg_render->height, &y);
+    get_float_or_percent_attribute (element, "width", svg_render->width, &width);
+    get_float_or_percent_attribute (element, "height", svg_render->height, &height);
+    get_float_or_percent_attribute (element, "rx", svg_render->width, &rx);
+    get_float_or_percent_attribute (element, "ry", svg_render->height, &ry);
+
+    if (rx == 0 && ry == 0) {
+        cairo_rectangle (svg_render->cr, x, y, width, height);
+    } else {
+        cairo_move_to (svg_render->cr, x + rx, y);
+        cairo_line_to (svg_render->cr, x + width - rx, y);
+        elliptical_arc (svg_render,    x + width - rx, y + ry, rx, ry, -M_PI/2, 0);
+        cairo_line_to (svg_render->cr, x + width, y + height - ry);
+        elliptical_arc (svg_render,    x + width - rx, y + height - ry, rx, ry, 0, M_PI/2);
+        cairo_line_to (svg_render->cr, x + rx, y + height);
+        elliptical_arc (svg_render,    x + rx, y + height - ry, rx, ry, M_PI/2, M_PI);
+        cairo_line_to (svg_render->cr, x, y + ry);
+        elliptical_arc (svg_render,    x + rx, y + ry, rx, ry, M_PI, -M_PI/2);
+    }
+
+    draw_path (svg_render);
+    return TRUE;
+}
+
+static cairo_bool_t
+render_element_circle (cairo_svg_glyph_render_t *svg_render,
+                       cairo_svg_element_t      *element,
+                       cairo_bool_t              end_tag)
+{
+    double cx = 0;
+    double cy = 0;
+    double r = 0;
+
+    if (end_tag ||
+        svg_render->graphics_state->mode == GS_NO_RENDER ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    get_float_or_percent_attribute (element, "cx", svg_render->width, &cx);
+    get_float_or_percent_attribute (element, "cy", svg_render->height, &cy);
+    get_float_or_percent_attribute (element, "r", svg_render->width, &r);
+
+    cairo_arc (svg_render->cr, cx, cy, r, 0, 2*M_PI);
+
+    draw_path (svg_render);
+    return TRUE;
+}
+
+static cairo_bool_t
+render_element_ellipse (cairo_svg_glyph_render_t *svg_render,
+                        cairo_svg_element_t      *element,
+                        cairo_bool_t              end_tag)
+{
+    double cx = 0;
+    double cy = 0;
+    double rx = 0;
+    double ry = 0;
+
+    if (end_tag ||
+        svg_render->graphics_state->mode == GS_NO_RENDER ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    get_float_or_percent_attribute (element, "cx", svg_render->width, &cx);
+    get_float_or_percent_attribute (element, "cy", svg_render->height, &cy);
+    get_float_or_percent_attribute (element, "rx", svg_render->width, &rx);
+    get_float_or_percent_attribute (element, "ry", svg_render->height, &ry);
+
+    elliptical_arc (svg_render, cx, cy, rx, ry, 0, 2*M_PI);
+    draw_path (svg_render);
+    return TRUE;
+}
+
+static cairo_bool_t
+render_element_line (cairo_svg_glyph_render_t *svg_render,
+                     cairo_svg_element_t      *element,
+                     cairo_bool_t              end_tag)
+{
+    double x1 = 0;
+    double y1 = 0;
+    double x2 = 0;
+    double y2 = 0;
+
+    if (end_tag ||
+        svg_render->graphics_state->mode == GS_NO_RENDER ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    get_float_or_percent_attribute (element, "x1", svg_render->width, &x1);
+    get_float_or_percent_attribute (element, "y1", svg_render->height, &y1);
+    get_float_or_percent_attribute (element, "x2", svg_render->width, &x2);
+    get_float_or_percent_attribute (element, "y2", svg_render->height, &y2);
+
+    cairo_move_to (svg_render->cr, x1, y1);
+    cairo_line_to (svg_render->cr, x2, y2);
+
+    draw_path (svg_render);
+    return TRUE;
+}
+
+static cairo_bool_t
+render_element_polyline (cairo_svg_glyph_render_t *svg_render,
+                         cairo_svg_element_t      *element,
+                         cairo_bool_t              end_tag)
+{
+    const char *p;
+    const char *end;
+    double x, y;
+    cairo_bool_t have_move = FALSE;
+
+    if (end_tag ||
+        svg_render->graphics_state->mode == GS_NO_RENDER ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    p = get_attribute (element, "points");
+    do {
+        end = get_path_params (p, 2, &x, &y);
+        if (!end) {
+            print_warning (svg_render, "points expected 2 numbers: %s", p);
+            break;
+        }
+        p = end;
+        if (!have_move) {
+            cairo_move_to (svg_render->cr, x, y);
+            have_move = TRUE;
+        } else {
+            cairo_line_to (svg_render->cr, x, y);
+        }
+        p = skip_space (p);
+    } while (p && *p);
+
+    if (string_equal (element->tag, "polygon"))
+        cairo_close_path (svg_render->cr);
+
+    draw_path (svg_render);
+    return TRUE;
+}
+
+static double
+angle_between_vectors (double ux,
+                       double uy,
+                       double vx,
+                       double vy)
+{
+    double dot = ux*vx + uy*vy;
+    double umag = sqrt (ux*ux + uy*uy);
+    double vmag = sqrt (vx*vx + vy*vy);
+    double c = dot/(umag*vmag);
+    if (c > 1.0)
+        c = 1.0;
+
+    if (c < -1.0)
+        c = -1.0;
+
+    double a = acos (c);
+    if (ux * vy - uy * vx < 0.0)
+        a = -a;
+
+    return a;
+}
+
+static void
+arc_path (cairo_t *cr,
+          double x1, double y1,
+          double x2, double y2,
+          double rx, double ry,
+          double rotate,
+          cairo_bool_t large_flag,
+          cairo_bool_t sweep_flag)
+{
+    double x1_, y1_, cx_, cy_;
+    double xm, ym, cx, cy;
+    double a, b, d;
+    double ux, uy, vx, vy;
+    double theta, delta_theta;
+    double epsilon;
+    cairo_matrix_t ctm;
+
+    cairo_get_matrix (cr, &ctm);
+    epsilon = _cairo_matrix_transformed_circle_major_axis (&ctm, cairo_get_tolerance (cr));
+
+    rotate *= M_PI/180.0;
+
+    /* Convert endpoint to center parameterization.
+     * See SVG 1.1 Appendix F.6. Step numbers are the steps in the appendix.
+     */
+
+    rx = fabs (rx);
+    ry = fabs (ry);
+    if (rx < epsilon || ry < epsilon) {
+        cairo_line_to (cr, x2, y2);
+        return;
+    }
+
+    if (fabs(x1 - x2) < epsilon && fabs(y1 - y2) < epsilon) {
+        cairo_line_to (cr, x2, y2);
+        return;
+    }
+
+    /* Step 1 */
+    xm = (x1 - x2)/2;
+    ym = (y1 - y2)/2;
+    x1_ = xm * cos (rotate) + ym * sin (rotate);
+    y1_ = xm * -sin (rotate) + ym * cos (rotate);
+
+    d = (x1_*x1_)/(rx*rx) + (y1_*y1_)/(ry*ry);
+    if (d > 1.0) {
+        d = sqrt (d);
+        rx *= d;
+        ry *= d;
+    }
+
+    /* Step 2 */
+    a = (rx*rx * y1_*y1_) + (ry*ry * x1_*x1_);
+    if (a == 0.0)
+        return;
+
+    b = (rx*rx * ry*ry) / a - 1.0;
+    if (b < 0)
+        b = 0.0;
+
+    d = sqrt(b);
+    if (large_flag == sweep_flag)
+        d = -d;
+
+    cx_ = d * rx*y1_/ry;
+    cy_ = d * -ry*x1_/rx;
+
+    /* Step 3 */
+    cx = cx_ * cos (rotate) - cy_ * sin (rotate) + (x1 + x2)/2;
+    cy = cx_ * sin (rotate) + cy_ * cos (rotate) + (y1 + y2)/2;
+
+    /* Step 4 */
+    ux = (x1_ - cx_)/rx;
+    uy = (y1_ - cy_)/ry;
+    vx = (-x1_ - cx_)/rx;
+    vy = (-y1_ - cy_)/ry;
+    theta = angle_between_vectors (1.0, 0, ux, uy);
+    delta_theta = angle_between_vectors (ux, uy, vx, vy);
+
+    if (!sweep_flag && delta_theta > 0)
+        delta_theta -= 2 * M_PI;
+    else if (sweep_flag && delta_theta < 0)
+        delta_theta += 2 * M_PI;
+
+    /* Now we can call cairo_arc() */
+    cairo_save (cr);
+    cairo_translate (cr, cx, cy);
+    cairo_scale (cr, rx, ry);
+    cairo_rotate (cr, theta);
+    if (delta_theta >= 0.0)
+        cairo_arc (cr, 0, 0, 1, 0, delta_theta);
+    else
+        cairo_arc_negative (cr, 0, 0, 1, 0, delta_theta);
+    cairo_restore (cr);
+}
+
+static void
+get_current_point (cairo_svg_glyph_render_t *svg_render, double *x, double *y)
+{
+    if (cairo_has_current_point (svg_render->cr)) {
+        cairo_get_current_point (svg_render->cr, x, y);
+    } else {
+        *x = 0;
+        *y = 0;
+    }
+}
+
+static void
+reflect_point (double origin_x, double origin_y, double *x, double *y)
+{
+    *x = 2*origin_x - *x;
+    *y = 2*origin_y - *y;
+}
+
+static cairo_bool_t
+render_element_path (cairo_svg_glyph_render_t *svg_render,
+                     cairo_svg_element_t      *element,
+                     cairo_bool_t              end_tag)
+{
+    double cur_x, cur_y;
+    double last_cp_x, last_cp_y;
+    double x, y, x1, y1, x2, y2;
+    double qx1, qy1, qx2, qy2;
+    double rx, ry, rotate, large_flag, sweep_flag;
+    cairo_bool_t rel, have_move;
+    enum { CUBIC, QUADRATIC, OTHER } last_op;
+
+    if (end_tag ||
+        svg_render->graphics_state->mode == GS_NO_RENDER ||
+        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
+        return FALSE;
+
+    last_op = OTHER;
+    const char *p = get_attribute (element, "d");
+    const char *end;
+    int op;
+
+    while (p) {
+        while (p && _cairo_isspace (*p))
+            p++;
+
+        if (!p || *p == 0)
+            break;
+
+        op = *p;
+        switch (op) {
+            case 'M':
+            case 'm':
+                rel = op == 'm';
+                p++;
+                have_move = FALSE;
+                do {
+                    end = get_path_params (p, 2, &x, &y);
+                    if (!end) {
+                        print_warning (svg_render, "path %c expected 2 numbers: %s", op, p);
+                        break;
+                    }
+                    p = end;
+                    if (rel) {
+                        get_current_point (svg_render, &cur_x, &cur_y);
+                        x += cur_x;
+                        y += cur_y;
+                    }
+                    if (!have_move) {
+                        cairo_move_to (svg_render->cr, x, y);
+                        have_move = TRUE;
+                    } else {
+                        cairo_line_to (svg_render->cr, x, y);
+                    }
+                    p = skip_space (p);
+                } while (p && *p && !_cairo_isalpha(*p));
+                last_op = OTHER;
+                break;
+            case 'Z':
+            case 'z':
+                p++;
+                cairo_close_path (svg_render->cr);
+                last_op = OTHER;
+                break;
+            case 'L':
+            case 'l':
+                rel = op == 'l';
+                p++;
+                do {
+                    end = get_path_params (p, 2, &x, &y);
+                    if (!end) {
+                        print_warning (svg_render, "path %c expected 2 numbers: %s", op, p);
+                        break;
+                    }
+                    p = end;
+                    if (rel) {
+                        get_current_point (svg_render, &cur_x, &cur_y);
+                        x += cur_x;
+                        y += cur_y;
+                    }
+                    cairo_line_to (svg_render->cr, x, y);
+                    p = skip_space (p);
+                } while (p && *p && !_cairo_isalpha(*p));
+                last_op = OTHER;
+                break;
+            case 'H':
+            case 'h':
+                rel = op == 'h';
+                p++;
+                do {
+                    end = get_path_params (p, 1, &x1);
+                    if (!end) {
+                        print_warning (svg_render, "path %c expected a number: %s", op, p);
+                        break;
+                    }
+                    p = end;
+                    get_current_point (svg_render, &cur_x, &cur_y);
+                    if (rel) {
+                        x1 += cur_x;
+                    }
+                    cairo_line_to (svg_render->cr, x1, cur_y);
+                    p = skip_space (p);
+                } while (p && *p && !_cairo_isalpha(*p));
+                last_op = OTHER;
+                break;
+            case 'V':
+            case 'v':
+                rel = op == 'v';
+                p++;
+                do {
+                    end = get_path_params (p, 1, &y1);
+                    if (!end) {
+                        print_warning (svg_render, "path %c expected a number: %s", op, p);
+                        break;
+                    }
+                    p = end;
+                    get_current_point (svg_render, &cur_x, &cur_y);
+                    if (rel) {
+                        y1 += cur_y;
+                    }
+                    cairo_line_to (svg_render->cr, cur_x, y1);
+                    p = skip_space (p);
+                } while (p && *p && !_cairo_isalpha(*p));
+                last_op = OTHER;
+                break;
+            case 'C':
+            case 'c':
+                rel = op == 'c';
+                p++;
+                do {
+                    end = get_path_params (p, 6, &x1, &y1, &x2, &y2, &x, &y);
+                    if (!end) {
+                        print_warning (svg_render, "path %c expected 6 numbers: %s", op, p);
+                        break;
+                    }
+                    p = end;
+                    if (rel) {
+                        get_current_point (svg_render, &cur_x, &cur_y);
+                        x1 += cur_x;
+                        y1 += cur_y;
+                        x2 += cur_x;
+                        y2 += cur_y;
+                        x += cur_x;
+                        y += cur_y;
+                    }
+                    cairo_curve_to (svg_render->cr, x1, y1, x2, y2, x, y);
+                    p = skip_space (p);
+                } while (p && *p && !_cairo_isalpha(*p));
+                last_op = CUBIC;
+                last_cp_x = x2;
+                last_cp_y = y2;
+                break;
+            case 'S':
+            case 's':
+                rel = op == 's';
+                p++;
+                do {
+                    end = get_path_params (p, 4, &x2, &y2, &x, &y);
+                    if (!end) {
+                        print_warning (svg_render, "path %c expected 4 numbers: %s", op, p);
+                        break;
+                    }
+                    p = end;
+                    get_current_point (svg_render, &cur_x, &cur_y);
+                    if (rel) {
+                        x2 += cur_x;
+                        y2 += cur_y;
+                        x += cur_x;
+                        y += cur_y;
+                    }
+                    if (last_op == CUBIC) {
+                        x1 = last_cp_x;
+                        y1 = last_cp_y;
+                        reflect_point (cur_x, cur_y, &x1, &y1);
+                    } else {
+                        x1 = cur_x;
+                        y1 = cur_y;
+                    }
+                    cairo_curve_to (svg_render->cr, x1, y1, x2, y2, x, y);
+                    last_op = CUBIC;
+                    last_cp_x = x2;
+                    last_cp_y = y2;
+                    p = skip_space (p);
+                } while (p && *p && !_cairo_isalpha(*p));
+                break;
+            case 'Q':
+            case 'q':
+                rel = op == 'q';
+                p++;
+                do {
+                    end = get_path_params (p, 4, &x1, &y1, &x, &y);
+                    if (!end) {
+                        print_warning (svg_render, "path %c expected 4 numbers: %s", op, p);
+                        break;
+                    }
+                    p = end;
+                    get_current_point (svg_render, &cur_x, &cur_y);
+                    if (rel) {
+                        x1 += cur_x;
+                        y1 += cur_y;
+                        x += cur_x;
+                        y += cur_y;
+                    }
+                    qx1 = cur_x + (x1 - cur_x)*2/3;
+                    qy1 = cur_y + (y1 - cur_y)*2/3;
+                    qx2 = x + (x1 - x)*2/3;
+                    qy2 = y + (y1 - y)*2/3;
+                    cairo_curve_to (svg_render->cr, qx1, qy1, qx2, qy2, x, y);
+                    p = skip_space (p);
+                } while (p && *p && !_cairo_isalpha(*p));
+                last_op = QUADRATIC;
+                last_cp_x = x1;
+                last_cp_y = y1;
+                break;
+            case 'T':
+            case 't':
+                rel = op == 't';
+                p++;
+                do {
+                    end = get_path_params (p, 2, &x, &y);
+                    if (!end) {
+                        print_warning (svg_render, "path %c expected 2 numbers: %s", op, p);
+                        break;
+                    }
+                    p = end;
+                    get_current_point (svg_render, &cur_x, &cur_y);
+                    if (rel) {
+                        x += cur_x;
+                        y += cur_y;
+                    }
+                    if (last_op == QUADRATIC) {
+                        x1 = last_cp_x;
+                        y1 = last_cp_y;
+                        reflect_point (cur_x, cur_y, &x1, &y1);
+                    } else {
+                        x1 = cur_x;
+                        y1 = cur_y;
+                    }
+                    qx1 = cur_x + (x1 - cur_x)*2/3;
+                    qy1 = cur_y + (y1 - cur_y)*2/3;
+                    qx2 = x + (x1 - x)*2/3;
+                    qy2 = y + (y1 - y)*2/3;
+                    cairo_curve_to (svg_render->cr, qx1, qy1, qx2, qy2, x, y);
+                    last_op = QUADRATIC;
+                    last_cp_x = x1;
+                    last_cp_y = y1;
+                    p = skip_space (p);
+                } while (p && *p && *p && !_cairo_isalpha(*p));
+                break;
+            case 'A':
+            case 'a':
+                rel = op == 'a';
+                p++;
+                do {
+                    end = get_path_params (p, 7, &rx, &ry, &rotate, &large_flag, &sweep_flag, &x, &y);
+                    if (!end) {
+                        print_warning (svg_render, "path %c expected 7 numbers: %s", op, p);
+                        break;
+                    }
+                    p = end;
+                    get_current_point (svg_render, &cur_x, &cur_y);
+                    if (rel) {
+                        x += cur_x;
+                        y += cur_y;
+                    }
+                    arc_path (svg_render->cr,
+                              cur_x, cur_y,
+                              x, y,
+                              rx, ry,
+                              rotate,
+                              large_flag > 0.5,
+                              sweep_flag > 0.5);
+                    p = skip_space (p);
+                } while (p && *p && !_cairo_isalpha(*p));
+                last_op = OTHER;
+                break;
+            default:
+                p = NULL;
+                break;
+        }
+    }
+
+    draw_path (svg_render);
+    return TRUE;
+}
+
+static void
+init_graphics_state (cairo_svg_glyph_render_t *svg_render)
+{
+    cairo_svg_graphics_state_t *gs;
+    double alpha;
+
+    gs = _cairo_malloc (sizeof (cairo_svg_graphics_state_t));
+    get_paint (svg_render, "black", &gs->fill);
+    get_paint (svg_render, "none", &gs->stroke);
+    gs->color.type = RGB;
+    if (cairo_pattern_get_rgba (svg_render->foreground_color,
+                                &gs->color.red,
+                                &gs->color.green,
+                                &gs->color.blue,
+                                &alpha) != CAIRO_STATUS_SUCCESS)
+    {
+        get_color (svg_render, "black", &gs->color);
+    }
+    gs->fill_opacity = 1.0;
+    gs->stroke_opacity = 1.0;
+    gs->opacity = 1.0;
+    gs->fill_rule = CAIRO_FILL_RULE_WINDING;
+    gs->clip_rule = CAIRO_FILL_RULE_WINDING;
+    gs->clip_path = NULL;
+    gs->dash_array = NULL;
+    gs->dash_offset = 0.0;
+    gs->mode = GS_RENDER;
+    gs->bbox.x = 0;
+    gs->bbox.y = 0;
+    gs->bbox.width = 0;
+    gs->bbox.height = 0;
+    gs->next = NULL;
+
+    svg_render->graphics_state = gs;
+
+    cairo_save (svg_render->cr);
+    cairo_set_source_rgb (svg_render->cr, 0, 0, 0);
+    cairo_set_line_width (svg_render->cr, 1.0);
+    cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_MITER);
+    cairo_set_miter_limit (svg_render->cr, 4.0);
+}
+
+#define MAX_DASHES 100
+static void update_dash (cairo_svg_glyph_render_t *svg_render,
+                         cairo_svg_element_t      *element)
+{
+    cairo_svg_graphics_state_t *gs = svg_render->graphics_state;
+    const char *p;
+    char *end;
+    double value;
+    double dash_array[MAX_DASHES];
+    int num_dashes = 0;
+    cairo_bool_t not_zero = FALSE;
+    
+    if (gs->dash_array == NULL || string_equal (gs->dash_array, "none")) {
+        cairo_set_dash (svg_render->cr, NULL, 0, 0);
+        return;
+    }
+
+    p = gs->dash_array;
+    while (*p && num_dashes < MAX_DASHES) {
+        while (*p && (*p == ',' || _cairo_isspace (*p)))
+            p++;
+
+        if (*p == 0)
+            break;
+
+        value = _cairo_strtod (p, &end);
+        if (end == p)
+            break;
+
+        p = end;
+        if (*p == '%') {
+            value *= svg_render->width / 100.0;
+            p++;
+        }
+
+        if (value < 0.0)
+            return;
+
+        if (value > 0.0)
+            not_zero = TRUE;
+
+        dash_array[num_dashes++] = value;
+    }
+
+    if (not_zero)
+        cairo_set_dash (svg_render->cr, dash_array, num_dashes, gs->dash_offset);
+}
+
+static cairo_bool_t
+pattern_requires_bbox (cairo_svg_glyph_render_t *svg_render,
+                       cairo_svg_element_t      *paint_server)
+{
+    const char *p;
+
+    if (string_equal (paint_server->tag, "linearGradient") ||
+        string_equal (paint_server->tag, "radialGradient"))
+    {
+        p = get_attribute (paint_server, "gradientUnits");
+        if (string_equal (p, "userSpaceOnUse"))
+            return FALSE;
+
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static cairo_bool_t
+clip_requires_bbox (cairo_svg_glyph_render_t *svg_render,
+                    const char               *clip_path)
+{
+    cairo_svg_element_t *element;
+    const char *p;
+
+    if (clip_path && strncmp (clip_path, "url", 3) == 0) {
+        element = lookup_url_element (svg_render, clip_path);
+        if (element) {
+            p = get_attribute (element, "clipPathUnits");
+            if (string_equal (p, "objectBoundingBox"))
+                return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+static cairo_bool_t
+need_bbox (cairo_svg_glyph_render_t *svg_render,
+           cairo_svg_element_t      *element)
+{
+    cairo_svg_graphics_state_t *gs = svg_render->graphics_state;
+    cairo_bool_t fill_needs_bbox = FALSE;
+    cairo_bool_t stroke_needs_bbox = FALSE;
+    cairo_bool_t clip_needs_bbox = FALSE;
+    
+    if (gs->mode != GS_RENDER)
+        return FALSE;
+
+    if (gs->fill.type == PAINT_SERVER && pattern_requires_bbox (svg_render, gs->fill.paint_server))
+        fill_needs_bbox = TRUE;
+
+    if (gs->stroke.type == PAINT_SERVER && pattern_requires_bbox (svg_render, gs->stroke.paint_server))
+        stroke_needs_bbox = TRUE;
+
+    if (clip_requires_bbox (svg_render, get_attribute (element, "clip-path")))
+        clip_needs_bbox = TRUE;
+
+    if (string_equal (element->tag, "circle") ||
+        string_equal (element->tag, "ellipse") ||
+        string_equal (element->tag, "path") ||
+        string_equal (element->tag, "polygon") ||
+        string_equal (element->tag, "rect"))
+    {
+        return fill_needs_bbox || stroke_needs_bbox || clip_needs_bbox;
+    }
+
+    if (string_equal (element->tag, "line") ||
+        string_equal (element->tag, "polyline"))
+    {
+        return stroke_needs_bbox || clip_needs_bbox;
+    }
+
+    if (string_equal (element->tag, "g") ||
+        string_equal (element->tag, "image") ||
+        string_equal (element->tag, "use"))
+    {
+        return clip_needs_bbox;
+    }
+    
+    return FALSE;
+}
+
+static cairo_bool_t
+call_element (cairo_svg_glyph_render_t *svg_render,
+              cairo_svg_element_t      *element,
+              cairo_bool_t              end_tag);
+
+static void
+update_graphics_state (cairo_svg_glyph_render_t *svg_render,
+                       cairo_svg_element_t      *element)
+{
+    double value;
+    const char *p;
+    cairo_svg_graphics_state_t *gs = svg_render->graphics_state;
+
+    p = get_attribute (element, "transform");
+    if (p) {
+        cairo_matrix_t m;
+        if (parse_transform (p, &m))
+            cairo_transform (svg_render->cr, &m);
+    }
+
+    /* The transform is all we need for bbox computation. The SVG spec
+     * excludes clipping and stroke-width from the bbox. */
+    if (gs->mode == GS_COMPUTE_BBOX)
+        return;
+    
+    p = get_attribute (element, "color");
+    if (p)
+        get_color (svg_render, p, &gs->color);
+
+    if (!get_float_attribute (element, "opacity", &gs->opacity))
+        gs->opacity = 1.0;
+
+    p = get_attribute (element, "fill");
+    if (p) {
+        get_paint (svg_render, p, &gs->fill);
+    }
+
+    get_float_attribute (element, "fill-opacity", &gs->fill_opacity);
+
+    gs->fill_rule = get_fill_rule_attribute (element, "fill-rule", gs->fill_rule);
+
+    gs->clip_rule = get_fill_rule_attribute (element, "fill-rule", gs->clip_rule);
+
+    p = get_attribute (element, "stroke");
+    if (p)
+        get_paint (svg_render, p, &gs->stroke);
+
+    if (get_float_or_percent_attribute (element, "stroke-width", svg_render->width, &value))
+        cairo_set_line_width (svg_render->cr, value);
+
+    p = get_attribute (element, "stroke-linecap");
+    if (string_equal (p, "butt"))
+        cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_BUTT);
+    else if (string_equal (p, "round"))
+        cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_ROUND);
+    else if (string_equal (p, "square"))
+        cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_SQUARE);
+
+    p = get_attribute (element, "stroke-linejoin");
+    if (string_equal (p, "miter"))
+        cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_MITER);
+    else if (string_equal (p, "round"))
+        cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_ROUND);
+    else if (string_equal (p, "bevel"))
+        cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_BEVEL);
+
+    if (get_float_attribute (element, "stroke-miterlimit", &value))
+        cairo_set_miter_limit (svg_render->cr, value);
+
+    p = get_attribute (element, "stroke-dasharray");
+    if (p)
+        gs->dash_array = strdup (p);
+
+    get_float_or_percent_attribute (element, "stroke-dashoffset", svg_render->width, &gs->dash_offset);
+    update_dash (svg_render, element);
+
+    /* Some elements may need the bounding box of the element thay are
+     * applied to.  As this recursively calls render_element on the
+     * same element while we are in render_element and setting up the
+     * graphics state, we check gs->mode to avoid re-entering the
+     * compute bbox code. The GS_COMPUTE_MODE flag is also used by
+     * render functions to ignore patterns and strokes (SVG spec
+     * ignores stroke with in bbox calculations) and just use a solid
+     * color.
+     */
+    if (gs->mode == GS_RENDER && need_bbox (svg_render, element)) {
+        cairo_surface_t *recording = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+        cairo_t *old_cr = svg_render->cr;
+        svg_render->cr = cairo_create (recording);
+        gs_mode_t old_mode = gs->mode;
+        gs->mode = GS_COMPUTE_BBOX;
+        /* To avoid recursing back into this function, we call the
+         * element directory then use render_element_tree to render
+         * the children */
+        call_element (svg_render, element, FALSE);
+        render_element_tree (svg_render, element, NULL, TRUE);
+        if (element->type == CONTAINER_ELEMENT)
+            call_element (svg_render, element, TRUE);
+        gs->mode = old_mode;
+        cairo_destroy (svg_render->cr);
+        svg_render->cr = old_cr;
+        cairo_recording_surface_ink_extents (recording,
+                                             &gs->bbox.x,
+                                             &gs->bbox.y,
+                                             &gs->bbox.width,
+                                             &gs->bbox.height);
+        cairo_surface_destroy (recording);
+    }
+
+    /* clip-path may require bbox */
+    p = get_attribute (element, "clip-path");
+    if (p && strncmp (p, "url", 3) == 0) {
+        element = lookup_url_element (svg_render, p);
+        if (element) {
+            gs_mode_t old_mode = gs->mode;
+            gs->mode = GS_CLIP;
+            render_element_tree (svg_render, element, NULL, FALSE);
+            cairo_set_fill_rule (svg_render->cr, gs->clip_rule);
+            cairo_clip (svg_render->cr);
+            gs->mode = old_mode;
+        }
+    }
+}
+
+static void
+save_graphics_state (cairo_svg_glyph_render_t *svg_render)
+{
+    cairo_svg_graphics_state_t *gs;
+
+    cairo_save (svg_render->cr);
+
+    gs = _cairo_malloc (sizeof (cairo_svg_graphics_state_t));
+    gs->fill           = svg_render->graphics_state->fill;
+    gs->stroke         = svg_render->graphics_state->stroke;
+    gs->color          = svg_render->graphics_state->color;
+    gs->fill_opacity   = svg_render->graphics_state->fill_opacity;
+    gs->stroke_opacity = svg_render->graphics_state->stroke_opacity;
+    gs->opacity        = svg_render->graphics_state->opacity;
+    gs->fill_rule      = svg_render->graphics_state->fill_rule;
+    gs->clip_rule      = svg_render->graphics_state->clip_rule;
+    gs->clip_path      = NULL;
+    gs->dash_array     = NULL;
+    if (svg_render->graphics_state->dash_array)
+        gs->dash_array = strdup (svg_render->graphics_state->dash_array);
+    gs->dash_offset    = svg_render->graphics_state->dash_offset;
+    gs->mode           = svg_render->graphics_state->mode;
+    gs->bbox           = svg_render->graphics_state->bbox;
+    gs->next           = svg_render->graphics_state;
+    svg_render->graphics_state = gs;
+}
+
+static void
+restore_graphics_state (cairo_svg_glyph_render_t *svg_render)
+{
+    cairo_svg_graphics_state_t *gs;
+
+    gs = svg_render->graphics_state;
+    svg_render->graphics_state = gs->next;
+    if (gs->clip_path)
+        cairo_path_destroy (gs->clip_path);
+    free (gs->dash_array);
+    free (gs);
+
+    cairo_restore (svg_render->cr);
+}
+
+/* render function returns TRUE if render_element_tree() is to render
+ * the child nodes, FALSE if render_element_tree() is to skip the
+ * child nodes.
+ */
+struct render_func {
+    const char *tag;
+    cairo_bool_t (*render) (cairo_svg_glyph_render_t *, cairo_svg_element_t *, cairo_bool_t);
+};
+
+/* Must be sorted */
+static const struct render_func render_funcs[] = {
+    { "circle", render_element_circle },
+    { "clipPath", render_element_clip_path },
+    { "defs", NULL },
+    { "desc", NULL },
+    { "ellipse", render_element_ellipse },
+    { "g", render_element_g },
+    { "image", render_element_image },
+    { "line", render_element_line },
+    { "linearGradient", render_element_linear_gradient },
+    { "metadata", NULL },
+    { "path", render_element_path },
+    { "polygon", render_element_polyline },
+    { "polyline", render_element_polyline },
+    { "radialGradient", render_element_radial_gradient },
+    { "rect", render_element_rect },
+    { "stop", render_element_stop },
+    { "svg", render_element_svg },
+    { "title", NULL },
+    { "use", render_element_use },
+};
+
+static int
+_render_func_compare (const void *a, const void *b)
+{
+    const struct render_func *render_func_a = a;
+    const struct render_func *render_func_b = b;
+
+    return strcmp (render_func_a->tag, render_func_b->tag);
+}
+
+static cairo_bool_t
+call_element (cairo_svg_glyph_render_t *svg_render,
+              cairo_svg_element_t      *element,
+              cairo_bool_t              end_tag)
+{
+    const struct render_func *func;
+    struct render_func key;
+    cairo_bool_t recurse = FALSE;
+
+    key.tag = element->tag;
+    key.render = NULL;
+    func = bsearch (&key,
+                    render_funcs,
+                    ARRAY_LENGTH (render_funcs),
+                    sizeof (struct render_func),
+                    _render_func_compare);
+    if (func) {
+        if (func->render) {
+            recurse = func->render (svg_render, element, end_tag);
+        }
+    } else {
+        print_warning (svg_render, "Unsupported element: %s", element->tag);
+    }
+
+    return recurse;
+}
+
+static cairo_bool_t
+render_element (cairo_svg_glyph_render_t *svg_render,
+                cairo_svg_element_t      *element,
+                cairo_bool_t              end_tag,
+                cairo_svg_element_t      *display_element)
+{
+    cairo_bool_t recurse = FALSE;
+    cairo_svg_graphics_state_t *gs;
+
+    /* Ignore elements if we have not seen "<svg>". Ignore
+      * "<svg>" if we have seen it */
+    if (svg_render->view_port_set) {
+        if (string_equal (element->tag, "svg"))
+            return FALSE;
+    } else {
+        if (!string_equal (element->tag, "svg"))
+            return FALSE;
+    }
+
+    if (element->type == EMPTY_ELEMENT ||
+        (element->type == CONTAINER_ELEMENT && !end_tag))
+    {
+        save_graphics_state (svg_render);
+        update_graphics_state (svg_render, element);
+    }
+
+    gs = svg_render->graphics_state;
+    if (gs->mode == GS_NO_RENDER && element == display_element)
+        gs->mode = GS_RENDER;
+
+    recurse = call_element (svg_render, element, end_tag);
+
+    if (element->type == EMPTY_ELEMENT ||
+        (element->type == CONTAINER_ELEMENT && end_tag))
+    {
+        restore_graphics_state (svg_render);
+    }
+
+    return recurse;
+}
+
+#define MAX_DEPTH 100
+
+static void
+render_element_tree (cairo_svg_glyph_render_t *svg_render,
+                     cairo_svg_element_t      *element,
+                     cairo_svg_element_t      *display_element,
+                     cairo_bool_t              children_only)
+{
+    if (!element)
+        return;
+
+    /* Avoid circular references by limiting the number of recursive
+     * calls to this function. */
+    if (svg_render->render_element_tree_depth > MAX_DEPTH)
+        return;
+
+    svg_render->render_element_tree_depth++;
+    if (element->type == EMPTY_ELEMENT && !children_only) {
+        render_element (svg_render, element, FALSE, display_element);
+
+    } else if (element->type == CONTAINER_ELEMENT) {
+        int num_elems;
+        cairo_bool_t recurse = TRUE;;
+
+        if (!children_only)
+            recurse = render_element (svg_render, element, FALSE, display_element);
+
+        /* We only render the children if the parent returned
+         * success. This is how we avoid rendering non display
+         * elements like gradients, <defs>, and anything not
+         * implemented. */
+        if (recurse) {
+            num_elems = _cairo_array_num_elements (&element->children);
+            for (int i = 0; i < num_elems; i++) {
+                cairo_svg_element_t *child;
+                _cairo_array_copy_element (&element->children, i, &child);
+                render_element_tree (svg_render, child, display_element, FALSE);
+            }
+        }
+
+        if (!children_only)
+            render_element (svg_render, element, TRUE, display_element);
+    }
+    svg_render->render_element_tree_depth--;
+}
+
+static void
+render_element_tree_id (cairo_svg_glyph_render_t *svg_render,
+                        const char               *element_id)
+{
+    cairo_svg_element_t *glyph_element = NULL;
+
+    if (element_id)
+        glyph_element = lookup_element (svg_render, element_id);
+
+    if (glyph_element)
+        svg_render->graphics_state->mode = GS_NO_RENDER;
+    else
+        svg_render->graphics_state->mode = GS_RENDER;
+
+    render_element_tree (svg_render, svg_render->tree, glyph_element, TRUE);
+}
+
+cairo_status_t
+_cairo_render_svg_glyph (const char           *svg_document,
+                         unsigned long         first_glyph,
+                         unsigned long         last_glyph,
+                         unsigned long         glyph,
+                         double                units_per_em,
+                         FT_Color             *palette,
+                         int                   num_palette_entries,
+                         cairo_t              *cr)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    cairo_svg_glyph_render_t *svg_render = _cairo_malloc (sizeof (cairo_svg_glyph_render_t));
+    if (unlikely (svg_render == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    svg_render->tree = NULL;
+    svg_render->ids = _cairo_hash_table_create (_element_id_equal);
+    if (unlikely (svg_render->ids == NULL)) {
+        free (svg_render);
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    svg_render->debug = 0;
+    const char *s = getenv ("CAIRO_DEBUG_SVG_RENDER");
+    if (s) {
+        if (strlen (s) > 0)
+            svg_render->debug = atoi (s);
+        else
+            svg_render->debug = ERROR;
+    }
+
+    svg_render->cr = cr;
+    svg_render->units_per_em = units_per_em;
+    svg_render->foreground_color = cairo_pattern_reference (cairo_get_source (cr));
+    svg_render->build_pattern.paint_server = NULL;
+    svg_render->build_pattern.pattern = NULL;
+    svg_render->build_pattern.type = BUILD_PATTERN_NONE;
+    svg_render->render_element_tree_depth = 0;
+    svg_render->view_port_set = FALSE;
+    svg_render->num_palette_entries = num_palette_entries;
+    svg_render->palette = palette;
+
+    init_graphics_state (svg_render);
+
+    print_info (svg_render, "Glyph ID: %ld", glyph);
+    print_info (svg_render, "Palette Entries: %d", num_palette_entries);
+    print_info (svg_render, "Units per EM: %f", units_per_em);
+    print_info (svg_render, "SVG Document:\n%s\n", svg_document);
+
+    /* First parse elements into a tree and populate ids hash table */
+    if (!parse_svg (svg_render, svg_document)) {
+        print_error (svg_render, "Parse SVG document failed");
+        status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
+        goto cleanup;
+    }
+
+#if SVG_RENDER_PRINT_FUNCTIONS
+    printf("\nTREE\n");
+    if (svg_render->tree) {
+        print_element (svg_render->tree, TRUE, 0);
+        printf("\n");
+    }
+#endif
+
+    /* Next, render glyph */
+    if (first_glyph == last_glyph) {
+        /* Render whole document */
+        render_element_tree_id (svg_render, NULL);
+    } else {
+        /* Render element with id "glyphID" where ID is glyph number. */
+
+        char glyph_id[30];
+        snprintf(glyph_id, sizeof(glyph_id), "#glyph%ld", glyph);
+        render_element_tree_id (svg_render, glyph_id);
+    }
+
+  cleanup:
+    if (svg_render->build_pattern.pattern)
+        cairo_pattern_destroy (svg_render->build_pattern.pattern);
+
+    if (svg_render->tree)
+        free_elements (svg_render, svg_render->tree);
+
+    while (svg_render->graphics_state)
+        restore_graphics_state (svg_render);
+
+    cairo_pattern_destroy (svg_render->foreground_color);
+
+    /* The hash entry for each element with an id is removed by
+     * free_elements() */
+    _cairo_hash_table_destroy (svg_render->ids);
+
+    free (svg_render);
+
+    return status;
+}
+
+#ifdef DEBUG_SVG_RENDER
+
+/**
+ * _cairo_debug_svg_render:
+ *
+ * Debug function for cairo-svg-glyph-render.c. Allows invoking the renderer from outside
+ * cairo to test with SVG documents, and to facilitate comparison with librsvg rendering.
+ * The viewport is .
+ *
+ * @cr: render target
+ * @svg_document: SVG Document
+ * @element: element within svg_document to render (eg "#glyph8"), or NULL to render entire document.
+ * @debug_level: 0 - quiet, 1 - print errors, 2 - print warnings, 3 - info
+ * @return TRUE on success, ie no errors, FALSE if error
+ */
+cairo_bool_t
+_cairo_debug_svg_render (cairo_t       *cr,
+                         const char    *svg_document,
+                         const char    *element,
+                         double         units_per_em,
+                         int            debug_level);
+
+cairo_bool_t
+_cairo_debug_svg_render (cairo_t       *cr,
+                         const char    *svg_document,
+                         const char    *element,
+                         double         units_per_em,
+                         int            debug_level)
+{
+    return _cairo_render_svg_glyph (svg_document,
+                                    1, 1, 1,
+                                    units_per_em,
+                                    NULL, 0,
+                                    cr) == CAIRO_STATUS_SUCCESS;
+}
+
+#endif /* DEBUG_SVG_RENDER */
+
+#endif /* HAVE_FT_SVG_DOCUMENT */
diff --git a/src/meson.build b/src/meson.build
index 0a14b3ba3..f8e2290f2 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -130,6 +130,7 @@ cairo_feature_sources = {
   ],
   'cairo-ft': [
     'cairo-ft-font.c',
+    'cairo-svg-glyph-render.c'
   ],
 
   'cairo-xlib': [
commit 392423aba38c0934235f17a40130d2e66e4490b0
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Mon Jun 13 21:56:02 2022 +0930

    Fix ink extents of recording surfaces within recording surfaces

diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index a118e338c..0e22b9aa9 100644
--- a/src/cairo-analysis-surface.c
+++ b/src/cairo-analysis-surface.c
@@ -926,6 +926,12 @@ _paint_return_success (void			*surface,
 		       const cairo_pattern_t	*source,
 		       const cairo_clip_t	*clip)
 {
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+        cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source;
+        if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+            return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+    }
+
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
@@ -936,6 +942,18 @@ _mask_return_success (void			*surface,
 		      const cairo_pattern_t	*mask,
 		      const cairo_clip_t	*clip)
 {
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+        cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source;
+        if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+            return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+    }
+
+    if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
+        cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask;
+        if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+            return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+    }
+
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
@@ -951,6 +969,12 @@ _stroke_return_success (void				*surface,
 			cairo_antialias_t		 antialias,
 			const cairo_clip_t		*clip)
 {
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+        cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source;
+        if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+            return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+    }
+
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
@@ -964,6 +988,12 @@ _fill_return_success (void			*surface,
 		      cairo_antialias_t		 antialias,
 		      const cairo_clip_t	*clip)
 {
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+        cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source;
+        if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+            return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+    }
+
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
@@ -976,6 +1006,12 @@ _show_glyphs_return_success (void			*surface,
 			     cairo_scaled_font_t	*scaled_font,
 			     const cairo_clip_t		*clip)
 {
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+        cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source;
+        if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+            return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+    }
+
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
commit 640e2dadfc96801c12f673f523c7391d72df613f
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri May 6 21:17:25 2022 +0930

    user fonts: ensure snap_x/y_scale are initialized

diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c
index fd989eaa0..03002d603 100644
--- a/src/cairo-user-font.c
+++ b/src/cairo-user-font.c
@@ -559,6 +559,8 @@ _cairo_user_font_face_scaled_font_create (void                        *abstract_
     {
 	double fixed_scale, x_scale, y_scale;
 
+	user_scaled_font->snap_x_scale = 1.0;
+	user_scaled_font->snap_y_scale = 1.0;
 	user_scaled_font->extent_scale = user_scaled_font->base.scale_inverse;
 	status = _cairo_matrix_compute_basis_scale_factors (&user_scaled_font->extent_scale,
 						      &x_scale, &y_scale,
commit 7c5e2758a405b180c52eedc7119cb761162e4b6c
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Mon May 2 21:30:53 2022 +0930

    ft: move CAIRO_SCALED_GLYPH_INFO_METRICS into separate function

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index b0a42b5ce..b60a57510 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2621,20 +2621,153 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t     *scaled_font,
     return status;
 }
 
+static cairo_int_status_t
+_cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
+				     cairo_scaled_glyph_t	*scaled_glyph,
+				     cairo_scaled_glyph_info_t	 info,
+				     FT_Face face,
+				     cairo_bool_t vertical_layout,
+				     int load_flags)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+    FT_Glyph_Metrics *metrics;
+    double x_factor, y_factor;
+    FT_GlyphSlot glyph;
+    cairo_bool_t scaled_glyph_loaded = FALSE;
+    cairo_text_extents_t fs_metrics;
+
+    cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
+
+    /* The font metrics for color glyphs should be the same as the
+     * outline glyphs. But just in case there aren't, request the
+     * color or outline metrics based on the font option and if
+     * the font has color.
+     */
+    int color_flag = 0;
+#ifdef FT_LOAD_COLOR
+    if (unscaled->have_color && scaled_font->base.options.color_mode != CAIRO_COLOR_MODE_NO_COLOR)
+	color_flag = FT_LOAD_COLOR;
+#endif
+    status = _cairo_ft_scaled_glyph_load_glyph (scaled_font,
+						scaled_glyph,
+						face,
+						load_flags | color_flag,
+						!hint_metrics,
+						vertical_layout);
+    if (unlikely (status))
+	return status;
+
+    glyph = face->glyph;
+    scaled_glyph_loaded = hint_metrics;
+
+    /*
+     * Compute font-space metrics
+     */
+    metrics = &glyph->metrics;
+
+    if (unscaled->x_scale == 0)
+	x_factor = 0;
+    else
+	x_factor = 1 / unscaled->x_scale;
+
+    if (unscaled->y_scale == 0)
+	y_factor = 0;
+    else
+	y_factor = 1 / unscaled->y_scale;
+
+    /*
+     * Note: Y coordinates of the horizontal bearing need to be negated.
+     *
+     * Scale metrics back to glyph space from the scaled glyph space returned
+     * by FreeType
+     *
+     * If we want hinted metrics but aren't asking for hinted glyphs from
+     * FreeType, then we need to do the metric hinting ourselves.
+     */
+
+    if (hint_metrics && (load_flags & FT_LOAD_NO_HINTING))
+    {
+	FT_Pos x1, x2;
+	FT_Pos y1, y2;
+	FT_Pos advance;
+
+	if (!vertical_layout) {
+	    x1 = (metrics->horiBearingX) & -64;
+	    x2 = (metrics->horiBearingX + metrics->width + 63) & -64;
+	    y1 = (-metrics->horiBearingY) & -64;
+	    y2 = (-metrics->horiBearingY + metrics->height + 63) & -64;
+
+	    advance = ((metrics->horiAdvance + 32) & -64);
+
+	    fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
+	    fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
+
+	    fs_metrics.width  = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
+	    fs_metrics.height  = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
+
+	    fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor;
+	    fs_metrics.y_advance = 0;
+	} else {
+	    x1 = (metrics->vertBearingX) & -64;
+	    x2 = (metrics->vertBearingX + metrics->width + 63) & -64;
+	    y1 = (metrics->vertBearingY) & -64;
+	    y2 = (metrics->vertBearingY + metrics->height + 63) & -64;
+
+	    advance = ((metrics->vertAdvance + 32) & -64);
+
+	    fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
+	    fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
+
+	    fs_metrics.width  = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
+	    fs_metrics.height  = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
+
+	    fs_metrics.x_advance = 0;
+	    fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor;
+	}
+    } else {
+	fs_metrics.width  = DOUBLE_FROM_26_6 (metrics->width) * x_factor;
+	fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor;
+
+	if (!vertical_layout) {
+	    fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor;
+	    fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor;
+
+	    if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE)
+		fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor;
+	    else
+		fs_metrics.x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor;
+	    fs_metrics.y_advance = 0 * y_factor;
+	} else {
+	    fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor;
+	    fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor;
+
+	    fs_metrics.x_advance = 0 * x_factor;
+	    if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE)
+		fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor;
+	    else
+		fs_metrics.y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor;
+	}
+    }
+
+    _cairo_scaled_glyph_set_metrics (scaled_glyph,
+				     &scaled_font->base,
+				     &fs_metrics);
+
+    return status;
+}
+
 static cairo_int_status_t
 _cairo_ft_scaled_glyph_init (void			*abstract_font,
 			     cairo_scaled_glyph_t	*scaled_glyph,
 			     cairo_scaled_glyph_info_t	 info,
 			     const cairo_color_t        *foreground_color)
 {
-    cairo_text_extents_t    fs_metrics;
     cairo_ft_scaled_font_t *scaled_font = abstract_font;
     cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
     FT_GlyphSlot glyph;
     FT_Face face;
     int load_flags = scaled_font->ft_options.load_flags;
-    FT_Glyph_Metrics *metrics;
-    double x_factor, y_factor;
     cairo_bool_t vertical_layout = FALSE;
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
     cairo_bool_t scaled_glyph_loaded = FALSE;
@@ -2662,123 +2795,14 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
     }
 
     if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) {
-
-	cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
-
-	/* The font metrics for color glyphs should be the same as the
-	 * outline glyphs. But just in case there aren't, request the
-	 * color or outline metrics based on the font option and if
-	 * the font has color.
-	 */
-	int color_flag = 0;
-#ifdef FT_LOAD_COLOR
-	if (unscaled->have_color && scaled_font->base.options.color_mode != CAIRO_COLOR_MODE_NO_COLOR)
-	    color_flag = FT_LOAD_COLOR;
-#endif
-	status = _cairo_ft_scaled_glyph_load_glyph (scaled_font,
-						    scaled_glyph,
-						    face,
-						    load_flags | color_flag,
-						    !hint_metrics,
-						    vertical_layout);
+	status = _cairo_ft_scaled_glyph_init_metrics (scaled_font,
+						      scaled_glyph,
+						      info,
+						      face,
+						      vertical_layout,
+						      load_flags);
 	if (unlikely (status))
 	    goto FAIL;
-
-	glyph = face->glyph;
-	scaled_glyph_loaded = hint_metrics;
-
-	/*
-	 * Compute font-space metrics
-	 */
-	metrics = &glyph->metrics;
-
-	if (unscaled->x_scale == 0)
-	    x_factor = 0;
-	else
-	    x_factor = 1 / unscaled->x_scale;
-
-	if (unscaled->y_scale == 0)
-	    y_factor = 0;
-	else
-	    y_factor = 1 / unscaled->y_scale;
-
-	/*
-	 * Note: Y coordinates of the horizontal bearing need to be negated.
-	 *
-	 * Scale metrics back to glyph space from the scaled glyph space returned
-	 * by FreeType
-	 *
-	 * If we want hinted metrics but aren't asking for hinted glyphs from
-	 * FreeType, then we need to do the metric hinting ourselves.
-	 */
-
-	if (hint_metrics && (load_flags & FT_LOAD_NO_HINTING))
-	{
-	    FT_Pos x1, x2;
-	    FT_Pos y1, y2;
-	    FT_Pos advance;
-
-	    if (!vertical_layout) {
-		x1 = (metrics->horiBearingX) & -64;
-		x2 = (metrics->horiBearingX + metrics->width + 63) & -64;
-		y1 = (-metrics->horiBearingY) & -64;
-		y2 = (-metrics->horiBearingY + metrics->height + 63) & -64;
-
-		advance = ((metrics->horiAdvance + 32) & -64);
-
-		fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
-		fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
-
-		fs_metrics.width  = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
-		fs_metrics.height  = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
-
-		fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor;
-		fs_metrics.y_advance = 0;
-	    } else {
-		x1 = (metrics->vertBearingX) & -64;
-		x2 = (metrics->vertBearingX + metrics->width + 63) & -64;
-		y1 = (metrics->vertBearingY) & -64;
-		y2 = (metrics->vertBearingY + metrics->height + 63) & -64;
-
-		advance = ((metrics->vertAdvance + 32) & -64);
-
-		fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
-		fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
-
-		fs_metrics.width  = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
-		fs_metrics.height  = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
-
-		fs_metrics.x_advance = 0;
-		fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor;
-	    }
-	 } else {
-	    fs_metrics.width  = DOUBLE_FROM_26_6 (metrics->width) * x_factor;
-	    fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor;
-
-	    if (!vertical_layout) {
-		fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor;
-		fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor;
-
-		if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE)
-		    fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor;
-		else
-		    fs_metrics.x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor;
-		fs_metrics.y_advance = 0 * y_factor;
-	    } else {
-		fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor;
-		fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor;
-
-		fs_metrics.x_advance = 0 * x_factor;
-		if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE)
-		    fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor;
-		else
-		    fs_metrics.y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor;
-	    }
-	 }
-
-	_cairo_scaled_glyph_set_metrics (scaled_glyph,
-					 &scaled_font->base,
-					 &fs_metrics);
     }
 
     if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) {


More information about the cairo-commit mailing list