[cairo-commit] 3 commits - doc/public src/cairo-ft-font.c src/cairo.h src/cairoint.h src/cairo-quartz-font.c src/cairo-scaled-font.c src/cairo-scaled-font-private.h src/cairo-user-font.c src/win32 test/reference test/user-font.c test/user-font-color.c test/user-font-proxy.c

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Thu Jan 26 11:55:42 UTC 2023


 doc/public/cairo-sections.txt                     |    2 
 src/cairo-ft-font.c                               |   13 -
 src/cairo-quartz-font.c                           |    2 
 src/cairo-scaled-font-private.h                   |    6 
 src/cairo-scaled-font.c                           |   62 ++++-
 src/cairo-user-font.c                             |  206 +++++++++++++++++--
 src/cairo.h                                       |   37 +--
 src/cairoint.h                                    |    5 
 src/win32/cairo-dwrite-font.cpp                   |    2 
 test/reference/user-font-color.image16.ref.png    |binary
 test/reference/user-font-color.pdf.ref.png        |binary
 test/reference/user-font-color.quartz.ref.png     |binary
 test/reference/user-font-color.recording.ref.png  |binary
 test/reference/user-font-color.ref.png            |binary
 test/reference/user-font-color.script.xfail.png   |binary
 test/reference/user-font-color.svg.xfail.png      |binary
 test/reference/user-font-color.xcb.ref.png        |binary
 test/reference/user-font-color.xlib.ref.png       |binary
 test/reference/user-font-proxy.pdf.argb32.ref.png |binary
 test/reference/user-font-proxy.pdf.rgb24.ref.png  |binary
 test/reference/user-font-proxy.ref.png            |binary
 test/reference/user-font-proxy.svg.ref.png        |binary
 test/reference/user-font.quartz.ref.png           |binary
 test/reference/user-font.recording.ref.png        |binary
 test/reference/user-font.ref.png                  |binary
 test/user-font-color.c                            |  232 +++++++++++++---------
 test/user-font-proxy.c                            |   31 ++
 test/user-font.c                                  |   49 ++--
 28 files changed, 465 insertions(+), 182 deletions(-)

New commits:
commit 055ca7fb09842caa2595cf7864fa3afd417ebd37
Merge: 0151a67e3 f35f757e8
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Thu Jan 26 11:55:40 2023 +0000

    Merge branch 'fix-user-fonts-foreground' into 'master'
    
    Add new cairo_user_scaled_font_get_foreground_source() function
    
    See merge request cairo/cairo!422

commit f35f757e8f8e19fa1a3ff656f3a10ee745ad00ec
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Jan 22 13:27:43 2023 +1030

    Update user font tests to test foreground colors

diff --git a/test/reference/user-font-color.image16.ref.png b/test/reference/user-font-color.image16.ref.png
index 6e3fe434c..25a95ddac 100644
Binary files a/test/reference/user-font-color.image16.ref.png and b/test/reference/user-font-color.image16.ref.png differ
diff --git a/test/reference/user-font-color.pdf.ref.png b/test/reference/user-font-color.pdf.ref.png
index 9cb145202..dc0962d70 100644
Binary files a/test/reference/user-font-color.pdf.ref.png and b/test/reference/user-font-color.pdf.ref.png differ
diff --git a/test/reference/user-font-color.quartz.ref.png b/test/reference/user-font-color.quartz.ref.png
index 712e9a3d9..bb76c16ab 100644
Binary files a/test/reference/user-font-color.quartz.ref.png and b/test/reference/user-font-color.quartz.ref.png differ
diff --git a/test/reference/user-font-color.recording.ref.png b/test/reference/user-font-color.recording.ref.png
index 68c058adc..e6f9f389e 100644
Binary files a/test/reference/user-font-color.recording.ref.png and b/test/reference/user-font-color.recording.ref.png differ
diff --git a/test/reference/user-font-color.ref.png b/test/reference/user-font-color.ref.png
index 9c33df7cd..e210c718d 100644
Binary files a/test/reference/user-font-color.ref.png and b/test/reference/user-font-color.ref.png differ
diff --git a/test/reference/user-font-color.script.xfail.png b/test/reference/user-font-color.script.xfail.png
index 61ceab505..19b45e714 100644
Binary files a/test/reference/user-font-color.script.xfail.png and b/test/reference/user-font-color.script.xfail.png differ
diff --git a/test/reference/user-font-color.svg.xfail.png b/test/reference/user-font-color.svg.xfail.png
new file mode 100644
index 000000000..fd10d6653
Binary files /dev/null and b/test/reference/user-font-color.svg.xfail.png differ
diff --git a/test/reference/user-font-color.xcb.ref.png b/test/reference/user-font-color.xcb.ref.png
new file mode 100644
index 000000000..45b323473
Binary files /dev/null and b/test/reference/user-font-color.xcb.ref.png differ
diff --git a/test/reference/user-font-color.xlib.ref.png b/test/reference/user-font-color.xlib.ref.png
new file mode 100644
index 000000000..45b323473
Binary files /dev/null and b/test/reference/user-font-color.xlib.ref.png differ
diff --git a/test/reference/user-font-proxy.pdf.argb32.ref.png b/test/reference/user-font-proxy.pdf.argb32.ref.png
index 749c61bcd..bda5eec9d 100644
Binary files a/test/reference/user-font-proxy.pdf.argb32.ref.png and b/test/reference/user-font-proxy.pdf.argb32.ref.png differ
diff --git a/test/reference/user-font-proxy.pdf.rgb24.ref.png b/test/reference/user-font-proxy.pdf.rgb24.ref.png
index 749c61bcd..bda5eec9d 100644
Binary files a/test/reference/user-font-proxy.pdf.rgb24.ref.png and b/test/reference/user-font-proxy.pdf.rgb24.ref.png differ
diff --git a/test/reference/user-font-proxy.ref.png b/test/reference/user-font-proxy.ref.png
index 95c04633f..65014847a 100644
Binary files a/test/reference/user-font-proxy.ref.png and b/test/reference/user-font-proxy.ref.png differ
diff --git a/test/reference/user-font-proxy.svg.ref.png b/test/reference/user-font-proxy.svg.ref.png
index 106583e0f..7a083e6d8 100644
Binary files a/test/reference/user-font-proxy.svg.ref.png and b/test/reference/user-font-proxy.svg.ref.png differ
diff --git a/test/reference/user-font.quartz.ref.png b/test/reference/user-font.quartz.ref.png
index f1a8d5752..665801d37 100644
Binary files a/test/reference/user-font.quartz.ref.png and b/test/reference/user-font.quartz.ref.png differ
diff --git a/test/reference/user-font.recording.ref.png b/test/reference/user-font.recording.ref.png
index aefe4c9fe..c1cb2bbc3 100644
Binary files a/test/reference/user-font.recording.ref.png and b/test/reference/user-font.recording.ref.png differ
diff --git a/test/reference/user-font.ref.png b/test/reference/user-font.ref.png
index 87a3f68e3..5a6af5490 100644
Binary files a/test/reference/user-font.ref.png and b/test/reference/user-font.ref.png differ
diff --git a/test/user-font-color.c b/test/user-font-color.c
index c35e693be..db8b6a173 100644
--- a/test/user-font-color.c
+++ b/test/user-font-color.c
@@ -32,16 +32,27 @@
 
 #include "cairo-test.h"
 
+#include <ctype.h>
 #include <stdlib.h>
 #include <stdio.h>
 
 
 #define BORDER 10
 #define TEXT_SIZE 64
-#define WIDTH  (TEXT_SIZE * 12 + 2*BORDER)
-#define HEIGHT (TEXT_SIZE + 2*BORDER)
+#define WIDTH  (TEXT_SIZE * 11 + 2*BORDER)
+#define HEIGHT (4*TEXT_SIZE + 5*BORDER)
 
-#define TEXT   "abcdefghijkl"
+#define TEXT          "abcdefghij"
+
+/* These characters will be drawn twice with a different foreground color */
+#define FG_TEXT       "acfh"
+
+/* Uppercase draws the same text but forces the use of the non-color
+ * render callback */
+#define TEXT_NO_COLOR    "ABCDEFGHIJ"
+#define FG_TEXT_NO_COLOR "ACFH"
+
+#define TEXT_PATH       "aabccdeffghhij"
 
 
 static cairo_status_t
@@ -55,40 +66,60 @@ test_scaled_font_init (cairo_scaled_font_t  *scaled_font,
 }
 
 static void
-render_glyph_solid (cairo_t *cr, double width, double height, cairo_bool_t color)
+render_glyph_solid (cairo_t *cr,
+                    double width,
+                    double height,
+                    cairo_bool_t color,
+                    cairo_scaled_font_t *scaled_font)
 {
-    cairo_pattern_t *pattern = cairo_pattern_reference(cairo_get_source (cr));
-
     if (color)
-        cairo_set_source_rgba (cr, 0, 1, 1, 0.5);
+        cairo_set_source_rgba (cr, 0.7, 0.2, 0.1, 0.9);
     cairo_rectangle (cr, 0, 0, width/2, height/2);
     cairo_fill (cr);
 
-    /* Draw the middle rectangle using the foreground color */
-    if (color)
-        cairo_set_source (cr, pattern);
+    if (color) {
+        if (scaled_font)
+            cairo_set_source (cr, cairo_user_scaled_font_get_foreground_marker (scaled_font));
+        else
+            cairo_set_source_rgba (cr, 0.2, 0.5, 0.3, 0.9);
+    }
     cairo_rectangle (cr, width/4, height/4, width/2, height/2);
     cairo_fill (cr);
 
     if (color)
-        cairo_set_source_rgba (cr, 1, 1, 0, 0.5);
+        cairo_set_source_rgba (cr, 0.2, 0.3, 0.5, 0.9);
     cairo_rectangle (cr, width/2, height/2, width/2, height/2);
     cairo_fill (cr);
-
-    cairo_pattern_destroy (pattern);
 }
 
 static void
-render_glyph_linear (cairo_t *cr, double width, double height, cairo_bool_t color)
+render_glyph_linear (cairo_t *cr,
+                     double width,
+                     double height,
+                     cairo_bool_t color,
+                     cairo_scaled_font_t *scaled_font)
 {
     cairo_pattern_t *pat;
+    cairo_pattern_t *fg;
 
     pat = cairo_pattern_create_linear (0.0, 0.0, width, height);
-    cairo_pattern_add_color_stop_rgba (pat, 0,   1, 0, 0, 1);
-    cairo_pattern_add_color_stop_rgba (pat, 0.5, 0, 1, 0, color ? 0.5 : 1);
-    cairo_pattern_add_color_stop_rgba (pat, 1,   0, 0, 1, 1);
-    cairo_set_source (cr, pat);
+    if (scaled_font) {
+        double r, g, b, a;
+
+        fg = cairo_user_scaled_font_get_foreground_source (scaled_font);
+        if (cairo_pattern_get_rgba (fg, &r, &g, &b, &a) != CAIRO_STATUS_SUCCESS) {
+            r = g = b = 0;
+            a = 1;
+        }
+        cairo_pattern_add_color_stop_rgba (pat, 0,  r, g, b, a);
+        cairo_pattern_add_color_stop_rgb  (pat, 1,  0, 0, 1);
+    } else {
+        cairo_pattern_add_color_stop_rgb (pat, 0,   1, 0.4, 0.2);
+        cairo_pattern_add_color_stop_rgb (pat, 0.5, 0.2, 1, 0.4);
+        cairo_pattern_add_color_stop_rgb (pat, 1,   0.2, 0.3, 1);
+    }
 
+    cairo_set_source (cr, pat);
     cairo_rectangle (cr, 0, 0, width, height);
     cairo_fill (cr);
 }
@@ -102,23 +133,23 @@ render_glyph_text (cairo_t *cr, double width, double height, cairo_bool_t color)
     cairo_set_font_size(cr, 0.5);
 
     if (color)
-        cairo_set_source_rgb (cr, 0.5, 0.5, 0);
+        cairo_set_source_rgb (cr, 0.5, 0.7, 0);
     cairo_move_to (cr, width*0.1, height/2);
     cairo_show_text (cr, "a");
 
     if (color)
-        cairo_set_source_rgb (cr, 0, 0.5, 0.5);
+        cairo_set_source_rgb (cr, 0, 0.5, 0.7);
     cairo_move_to (cr, width*0.4, height*0.9);
     cairo_show_text (cr, "z");
 }
 
 static cairo_status_t
-test_scaled_font_render_color_glyph (cairo_scaled_font_t  *scaled_font,
-                                     unsigned long         glyph,
-                                     cairo_t              *cr,
-                                     cairo_text_extents_t *metrics)
+test_scaled_font_render_glyph_common (cairo_scaled_font_t  *scaled_font,
+                                      unsigned long         glyph,
+                                      cairo_t              *cr,
+                                      cairo_text_extents_t *metrics,
+                                      cairo_bool_t          color)
 {
-    cairo_status_t status = CAIRO_STATUS_SUCCESS;
     double width = 0.5;
     double height = 0.8;
 
@@ -126,101 +157,80 @@ test_scaled_font_render_color_glyph (cairo_scaled_font_t  *scaled_font,
     cairo_translate (cr,  0.125, -0.6);
     switch (glyph) {
         case 'a':
-            render_glyph_solid (cr, width, height, TRUE);
+            render_glyph_solid (cr, width, height, color, scaled_font);
             break;
         case 'b':
-            render_glyph_linear (cr, width, height, TRUE);
+            render_glyph_solid (cr, width, height, color, NULL);
             break;
         case 'c':
-            render_glyph_text (cr, width, height, TRUE);
+            render_glyph_linear (cr, width, height, color, scaled_font);
             break;
-
-            /* Ensure that the following glyphs are rendered with
-             * test_scaled_font_render_glyph() even if we draw
-             * something before returning.
-             */
         case 'd':
-            render_glyph_solid (cr, width, height, TRUE);
-            status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
+            render_glyph_linear (cr, width, height, color, NULL);
             break;
         case 'e':
-            render_glyph_linear (cr, width, height, TRUE);
-            status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
+            render_glyph_text (cr, width, height, color);
             break;
         case 'f':
-            render_glyph_solid (cr, width, height, TRUE);
-            status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
+            cairo_push_group (cr);
+            render_glyph_solid (cr, width, height, color, scaled_font);
+            cairo_pop_group_to_source (cr);
+            cairo_paint (cr);
             break;
         case 'g':
             cairo_push_group (cr);
-            render_glyph_solid (cr, width, height, TRUE);
+            render_glyph_solid (cr, width, height, color, NULL);
             cairo_pop_group_to_source (cr);
             cairo_paint (cr);
             break;
         case 'h':
             cairo_push_group (cr);
-            render_glyph_linear (cr, width, height, TRUE);
+            render_glyph_linear (cr, width, height, color, scaled_font);
             cairo_pop_group_to_source (cr);
             cairo_paint (cr);
             break;
         case 'i':
             cairo_push_group (cr);
-            render_glyph_text (cr, width, height, TRUE);
+            render_glyph_linear (cr, width, height, color, NULL);
             cairo_pop_group_to_source (cr);
             cairo_paint (cr);
             break;
-        default:
-            status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
-    }
-
-    return status;
-}
-
-static cairo_status_t
-test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
-			       unsigned long         glyph,
-			       cairo_t              *cr,
-			       cairo_text_extents_t *metrics)
-{
-    double width = 0.5;
-    double height = 0.8;
-    metrics->x_advance = 0.75;
-    cairo_translate (cr,  0.125, -0.6);
-    switch (glyph) {
-        case 'd':
-            render_glyph_solid (cr, width, height, FALSE);
-            break;
-        case 'e':
-            render_glyph_linear (cr, width, height, FALSE);
-            break;
-        case 'f':
-            render_glyph_text (cr, width, height, FALSE);
-            break;
         case 'j':
             cairo_push_group (cr);
-            render_glyph_solid (cr, width, height, FALSE);
-            cairo_pop_group_to_source (cr);
-            cairo_paint (cr);
-            break;
-        case 'k':
-            cairo_push_group (cr);
-            render_glyph_linear (cr, width, height, FALSE);
-            cairo_pop_group_to_source (cr);
-            cairo_paint (cr);
-            break;
-        case 'l':
-            cairo_push_group (cr);
-            render_glyph_text (cr, width, height, FALSE);
+            render_glyph_text (cr, width, height, color);
             cairo_pop_group_to_source (cr);
             cairo_paint (cr);
             break;
-        default:
-            return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
     }
 
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_status_t
+test_scaled_font_render_color_glyph_callback (cairo_scaled_font_t  *scaled_font,
+                                     unsigned long         glyph,
+                                     cairo_t              *cr,
+                                     cairo_text_extents_t *metrics)
+{
+    if (isupper(glyph))
+        return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
+
+    return test_scaled_font_render_glyph_common (scaled_font, glyph, cr, metrics, TRUE);
+}
+
+static cairo_status_t
+test_scaled_font_render_glyph_callback (cairo_scaled_font_t  *scaled_font,
+                                        unsigned long         glyph,
+                                        cairo_t              *cr,
+                                        cairo_text_extents_t *metrics)
+{
+    int c = glyph;
+    if (isupper(c))
+        c = tolower(c);
+
+    return test_scaled_font_render_glyph_common (scaled_font, c, cr, metrics, FALSE);
+}
+
 static cairo_status_t
 _user_font_face_create (cairo_font_face_t **out)
 {
@@ -229,13 +239,35 @@ _user_font_face_create (cairo_font_face_t **out)
 
     user_font_face = cairo_user_font_face_create ();
     cairo_user_font_face_set_init_func (user_font_face, test_scaled_font_init);
-    cairo_user_font_face_set_render_color_glyph_func (user_font_face, test_scaled_font_render_color_glyph);
-    cairo_user_font_face_set_render_glyph_func (user_font_face, test_scaled_font_render_glyph);
+    cairo_user_font_face_set_render_color_glyph_func (user_font_face,
+                                                      test_scaled_font_render_color_glyph_callback);
+    cairo_user_font_face_set_render_glyph_func (user_font_face,
+                                                test_scaled_font_render_glyph_callback);
 
     *out = user_font_face;
     return CAIRO_STATUS_SUCCESS;
 }
 
+/* Any text characters that are in fg_text will be drawn with a different color */
+static void
+draw_line (cairo_t *cr, const char *text, const char *fg_text)
+{
+    char buf[10];
+
+    for (unsigned i = 0; i < strlen(text); i++) {
+        buf[0] = text[i];
+        buf[1] = 0;
+
+        if (strchr (fg_text, text[i])) {
+            cairo_set_source_rgb (cr, 1, 0, 0);
+            cairo_show_text (cr, buf);
+        }
+
+        cairo_set_source_rgb (cr, 0, 1, 0);
+        cairo_show_text (cr, buf);
+    }
+}
+
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
@@ -244,6 +276,7 @@ draw (cairo_t *cr, int width, int height)
     cairo_font_extents_t font_extents;
     cairo_text_extents_t extents;
     cairo_status_t status;
+    cairo_font_options_t *font_options;
 
     cairo_set_source_rgb (cr, 1, 1, 1);
     cairo_paint (cr);
@@ -285,10 +318,31 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_line_width (cr, 2);
     cairo_stroke (cr);
 
-    /* text in color */
-    cairo_set_source_rgb (cr, 0, 0.3, 0);
+    /* Line 1: text in color */
     cairo_move_to (cr, BORDER, BORDER + font_extents.ascent);
-    cairo_show_text (cr, text);
+    draw_line (cr, TEXT, FG_TEXT);
+
+    /* Line 2: text in non-color (color render callback returns
+     * CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED.
+     */
+    cairo_move_to (cr, BORDER, BORDER + font_extents.height + 1*BORDER + font_extents.ascent);
+    draw_line (cr, TEXT_NO_COLOR, FG_TEXT_NO_COLOR);
+
+    /* Line 3: Filled version of color text in blue */
+    cairo_move_to (cr, BORDER, BORDER + 2*font_extents.height + 2*BORDER + font_extents.ascent);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_text_path (cr, TEXT_PATH);
+    cairo_fill (cr);
+
+    /* Line 4: color glyphs with CAIRO_COLOR_MODE_NO_COLOR font option. */
+    font_options = cairo_font_options_create ();
+    cairo_get_font_options (cr, font_options);
+    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_move_to (cr, BORDER, BORDER + 3*font_extents.height + 3*BORDER + font_extents.ascent);
+    draw_line (cr, TEXT, FG_TEXT);
 
     return CAIRO_TEST_SUCCESS;
 }
diff --git a/test/user-font-proxy.c b/test/user-font-proxy.c
index e4063f0eb..42f51b602 100644
--- a/test/user-font-proxy.c
+++ b/test/user-font-proxy.c
@@ -40,7 +40,9 @@
 #else
  #define HEIGHT WIDTH
 #endif
-#define TEXT   "geez... cairo user-font"
+
+#define TEXT1   "cairo user-font."
+#define TEXT2   " zg"
 
 static cairo_user_data_key_t fallback_font_key;
 
@@ -148,11 +150,16 @@ _user_font_face_create (cairo_font_face_t **out)
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
-    const char text[] = TEXT;
     cairo_font_extents_t font_extents;
     cairo_text_extents_t extents;
     cairo_font_face_t *font_face;
     cairo_status_t status;
+    char full_text[100];
+
+    strcpy(full_text, TEXT1);
+    strcat(full_text, TEXT2);
+    strcat(full_text, TEXT2);
+    strcat(full_text, TEXT2);
 
     cairo_set_source_rgb (cr, 1, 1, 1);
     cairo_paint (cr);
@@ -174,7 +181,7 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_font_size (cr, TEXT_SIZE);
 
     cairo_font_extents (cr, &font_extents);
-    cairo_text_extents (cr, text, &extents);
+    cairo_text_extents (cr, full_text, &extents);
 
     /* logical boundaries in red */
     cairo_move_to (cr, 0, BORDER);
@@ -199,16 +206,26 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_line_width (cr, 2);
     cairo_stroke (cr);
 
-    /* text in gray */
-    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    /* TEXT1 in black */
     cairo_move_to (cr, BORDER, BORDER + font_extents.ascent);
-    cairo_show_text (cr, text);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_show_text (cr, TEXT1);
 
+    /* Draw TEXT2 three times with three different foreground colors.
+     * This checks that cairo uses the foreground color and does not cache
+     * glyph images when the foreground color changes.
+     */
+    cairo_show_text (cr, TEXT2);
+    cairo_set_source_rgb (cr, 0, 0.5, 0);
+    cairo_show_text (cr, TEXT2);
+    cairo_set_source_rgb (cr, 0.2, 0.5, 0.5);
+    cairo_show_text (cr, TEXT2);
 
     /* filled version of text in light blue */
     cairo_set_source_rgb (cr, 0, 0, 1);
     cairo_move_to (cr, BORDER, BORDER + font_extents.height + BORDER + font_extents.ascent);
-    cairo_text_path (cr, text);
+    cairo_text_path (cr, full_text);
     cairo_fill (cr);
 
     return CAIRO_TEST_SUCCESS;
diff --git a/test/user-font.c b/test/user-font.c
index d02a90f4d..a8aed23b3 100644
--- a/test/user-font.c
+++ b/test/user-font.c
@@ -34,7 +34,7 @@
 
 #define BORDER 10
 #define TEXT_SIZE 64
-#define WIDTH  (TEXT_SIZE * 15 + 2*BORDER)
+#define WIDTH  (TEXT_SIZE * 16 + 2*BORDER)
 #ifndef ROTATED
  #define HEIGHT ((TEXT_SIZE + 2*BORDER)*3)
 #else
@@ -42,7 +42,7 @@
 #endif
 
 #define TEXT1   "cairo user-font."
-#define TEXT2   " zg."
+#define TEXT2   " zg"
 
 #define END_GLYPH 0
 #define STROKE 126
@@ -202,6 +202,24 @@ _user_font_face_create (cairo_font_face_t **out, cairo_bool_t color_render)
     return CAIRO_STATUS_SUCCESS;
 }
 
+static void
+draw_line (cairo_t *cr)
+{
+    /* TEXT1 in black */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_show_text (cr, TEXT1);
+
+    /* Draw TEXT2 three times with three different foreground colors.
+     * This checks that cairo uses the foreground color and does not cache
+     * glyph images when the foreground color changes.
+     */
+    cairo_show_text (cr, TEXT2);
+    cairo_set_source_rgb (cr, 0, 0.5, 0);
+    cairo_show_text (cr, TEXT2);
+    cairo_set_source_rgb (cr, 0.2, 0.5, 0.5);
+    cairo_show_text (cr, TEXT2);
+}
+
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
@@ -214,6 +232,7 @@ draw (cairo_t *cr, int width, int height)
     strcpy(full_text, TEXT1);
     strcat(full_text, TEXT2);
     strcat(full_text, TEXT2);
+    strcat(full_text, TEXT2);
 
     cairo_set_source_rgb (cr, 1, 1, 1);
     cairo_paint (cr);
@@ -260,21 +279,14 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_line_width (cr, 2);
     cairo_stroke (cr);
 
-    /* First line. Text in black, except first "zg." in green */
-    cairo_set_source_rgb (cr, 0, 0, 0);
+    /* First line. TEXT1 in black. TEXT2 in different colors. */
     cairo_move_to (cr, BORDER, BORDER + font_extents.ascent);
-    cairo_show_text (cr, TEXT1);
-    cairo_set_source_rgb (cr, 0, 1, 0);
-    cairo_show_text (cr, TEXT2);
-    cairo_set_source_rgb (cr, 0, 0, 0);
-    cairo_show_text (cr, TEXT2);
+    draw_line (cr);
 
-    /* Now draw the second line using the render_color_glyph callback. The
-     * output should be the same because same render function is used
-     * and the render function does not set a color. This exercises
-     * the paint color glyph with foreground color code path and
-     * ensures cairo updates the glyph image when the foreground color
-     * changes.
+    /* Now draw the second line using the render_color_glyph
+     * callback. The text should be all black because the default
+     * color of render function is used instead of the foreground
+     * color.
      */
     status = _user_font_face_create (&font_face, TRUE);
     if (status) {
@@ -287,13 +299,8 @@ draw (cairo_t *cr, int width, int height)
 
     cairo_set_font_size (cr, TEXT_SIZE);
 
-    /* text in black, except first "zg." in green */
     cairo_move_to (cr, BORDER, BORDER + font_extents.height + 2*BORDER + font_extents.ascent);
-    cairo_show_text (cr, TEXT1);
-    cairo_set_source_rgb (cr, 0, 1, 0);
-    cairo_show_text (cr, TEXT2);
-    cairo_set_source_rgb (cr, 0, 0, 0);
-    cairo_show_text (cr, TEXT2);
+    draw_line (cr);
 
     /* Third line. Filled version of text in blue */
     cairo_set_source_rgb (cr, 0, 0, 1);
commit e93d175aac6805692d660f1f6444e6b21492e2a7
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Jan 22 13:27:53 2023 +1030

    Add new cairo_user_scaled_font_get_foreground_source() function
    
    The previous approach using foreground colors in user fonts does not
    work for gradients since the foreground color is not available at the
    time of recording.
    
    Add a new function cairo_user_scaled_font_get_foreground_source() that
    can be called by the color render function to retrieve the foreground
    pattern. Calling this function signals to cairo that the foreground
    color is used. In this case cairo will call the render function
    whenever the foreground color has changed.

diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt
index 2aeecdca1..4b07fa3c7 100644
--- a/doc/public/cairo-sections.txt
+++ b/doc/public/cairo-sections.txt
@@ -57,6 +57,8 @@ cairo_user_font_face_set_unicode_to_glyph_func
 cairo_user_font_face_get_unicode_to_glyph_func
 cairo_user_font_face_set_text_to_glyphs_func
 cairo_user_font_face_get_text_to_glyphs_func
+cairo_user_scaled_font_get_foreground_marker
+cairo_user_scaled_font_get_foreground_source
 </SECTION>
 
 <SECTION>
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 254dcf25e..e6a8d20e3 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2685,7 +2685,7 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t     *scaled_font,
 	    _cairo_scaled_glyph_set_color_surface (scaled_glyph,
 						   &scaled_font->base,
 						   surface,
-						   uses_foreground_color);
+						   uses_foreground_color ? foreground_color : NULL);
 
 	    scaled_glyph->color_glyph = TRUE;
 	} else {
@@ -2797,7 +2797,8 @@ _cairo_ft_scaled_glyph_init_record_colr_v0_glyph (cairo_ft_scaled_font_t *scaled
 
     _cairo_scaled_glyph_set_recording_surface (scaled_glyph,
 					       &scaled_font->base,
-					       recording_surface);
+					       recording_surface,
+					       NULL);
     return status;
 }
 #endif
@@ -2862,7 +2863,8 @@ _cairo_ft_scaled_glyph_init_record_colr_v1_glyph (cairo_ft_scaled_font_t *scaled
 
     _cairo_scaled_glyph_set_recording_surface (scaled_glyph,
 					       &scaled_font->base,
-					       recording_surface);
+					       recording_surface,
+					       NULL);
 
     scaled_glyph->color_glyph = TRUE;
     scaled_glyph->color_glyph_set = TRUE;
@@ -3009,7 +3011,8 @@ _cairo_ft_scaled_glyph_init_record_svg_glyph (cairo_ft_scaled_font_t *scaled_fon
 
     _cairo_scaled_glyph_set_recording_surface (scaled_glyph,
 					       &scaled_font->base,
-					       recording_surface);
+					       recording_surface,
+					       NULL);
 
     scaled_glyph->color_glyph = TRUE;
     scaled_glyph->color_glyph_set = TRUE;
@@ -3118,7 +3121,7 @@ _cairo_ft_scaled_glyph_init_surface_for_recording_surface (cairo_ft_scaled_font_
     _cairo_scaled_glyph_set_color_surface (scaled_glyph,
 					   &scaled_font->base,
 					   (cairo_image_surface_t *)surface,
-					   foreground_used);
+					   foreground_used ? foreground_color : NULL);
     surface = NULL;
 
     if (surface)
diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c
index 641a2dfc7..1e7531356 100644
--- a/src/cairo-quartz-font.c
+++ b/src/cairo-quartz-font.c
@@ -656,7 +656,7 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
     cairo_surface_mark_dirty (&surface->base);
 
     if (is_color)
-	_cairo_scaled_glyph_set_color_surface (scaled_glyph, &font->base, surface, fg_color != NULL);
+	_cairo_scaled_glyph_set_color_surface (scaled_glyph, &font->base, surface, fg_color);
     else
 	_cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);
 
diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h
index bcc13bdc8..8c10b60c7 100644
--- a/src/cairo-scaled-font-private.h
+++ b/src/cairo-scaled-font-private.h
@@ -149,8 +149,12 @@ struct _cairo_scaled_glyph {
     cairo_list_t            dev_privates;
 
     cairo_color_t           foreground_color;   /* only used for color glyphs */
+
+    /* TRUE if the recording_surface required the foreground_color to render. */
+    unsigned                recording_uses_foreground_color : 1;
+
     /* TRUE if the color_surface required the foreground_color to render. */
-    unsigned                uses_foreground_color : 1;
+    unsigned                image_uses_foreground_color : 1;
 
     /* TRUE if color_glyph specifies if glyph is color or non color, FALSE if glyph color type unknown. */
     unsigned                color_glyph_set : 1;
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index cc52ed577..d7e3f1e4b 100755
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -2653,10 +2653,19 @@ _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph,
 	scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_PATH;
 }
 
+/**
+ * _cairo_scaled_glyph_set_recording_surface:
+ * @scaled_glyph: a #cairo_scaled_glyph_t
+ * @scaled_font: a #cairo_scaled_font_t
+ * @recording_surface: The recording surface
+ * @foreground_color: The foreground color that was used to record the
+ * glyph, or NULL if foreground color not required.
+ */
 void
 _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
-					   cairo_scaled_font_t *scaled_font,
-					   cairo_surface_t *recording_surface)
+					   cairo_scaled_font_t  *scaled_font,
+					   cairo_surface_t      *recording_surface,
+					   const cairo_color_t * foreground_color)
 {
     if (scaled_glyph->recording_surface != NULL) {
 	cairo_surface_finish (scaled_glyph->recording_surface);
@@ -2664,6 +2673,9 @@ _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
     }
 
     scaled_glyph->recording_surface = recording_surface;
+    scaled_glyph->recording_uses_foreground_color = foreground_color != NULL;
+    if (foreground_color)
+	scaled_glyph->foreground_color = *foreground_color;
 
     if (recording_surface != NULL)
 	scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
@@ -2671,11 +2683,19 @@ _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
 	scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
 }
 
+/**
+ * _cairo_scaled_glyph_set_color_surface:
+ * @scaled_glyph: a #cairo_scaled_glyph_t
+ * @scaled_font: a #cairo_scaled_font_t
+ * @surface: The image surface
+ * @foreground_color: The foreground color that was used to render the
+ * glyph, or NULL if foreground color not required.
+ */
 void
-_cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph,
-	                               cairo_scaled_font_t *scaled_font,
+_cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t  *scaled_glyph,
+	                               cairo_scaled_font_t   *scaled_font,
 	                               cairo_image_surface_t *surface,
-				       cairo_bool_t uses_foreground_color)
+				       const cairo_color_t   *foreground_color)
 {
     if (scaled_glyph->color_surface != NULL)
 	cairo_surface_destroy (&scaled_glyph->color_surface->base);
@@ -2683,7 +2703,9 @@ _cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph,
     /* sanity check the backend glyph contents */
     _cairo_debug_check_image_surface_is_defined (&surface->base);
     scaled_glyph->color_surface = surface;
-    scaled_glyph->uses_foreground_color = uses_foreground_color;
+    scaled_glyph->image_uses_foreground_color = foreground_color != NULL;
+    if (foreground_color)
+	scaled_glyph->foreground_color = *foreground_color;
 
     if (surface != NULL)
 	scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE;
@@ -2814,8 +2836,11 @@ _cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font,
  * @index: the glyph to create
  * @info: a #cairo_scaled_glyph_info_t marking which portions of
  * the glyph should be filled in.
- * @foreground_color - foreground color to use when rendering color fonts. Use NULL
- * if not requesting CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE or foreground color is unknown.
+ * @foreground_color - foreground color to use when rendering color
+ * fonts. Use NULL if not requesting
+ * CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE or
+ * CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, or foreground color is
+ * unknown.
  * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph
  * is returned.
  *
@@ -2909,14 +2934,23 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
 	scaled_glyph->color_glyph_set && !scaled_glyph->color_glyph)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
-    /* If requesting a color surface for a glyph that has used the
-     * foreground color to render the color_surface, and the
-     * foreground color has changed, request a new image. */
-    if ((info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) &&
-	scaled_glyph->uses_foreground_color &&
+    /* If requesting a color surface or recording for a glyph that has
+     * used the foreground color to render the color_surface, and the
+     * foreground color has changed, request a new image and/or
+     * recording. */
+
+    if (info & CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE &&
+	scaled_glyph->recording_uses_foreground_color &&
+	!_cairo_color_equal (foreground_color, &scaled_glyph->foreground_color))
+    {
+	need_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
+    }
+
+    if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE &&
+	scaled_glyph->image_uses_foreground_color &&
 	!_cairo_color_equal (foreground_color, &scaled_glyph->foreground_color))
     {
-	need_info |= CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE;
+	    need_info |= CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE;
     }
 
     if (need_info) {
diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c
index 80cd4c303..913395fd9 100644
--- a/src/cairo-user-font.c
+++ b/src/cairo-user-font.c
@@ -95,13 +95,19 @@ typedef struct _cairo_user_scaled_font {
     double snap_x_scale;
     double snap_y_scale;
 
+    cairo_pattern_t *foreground_marker;
+    cairo_pattern_t *foreground_pattern;
+    cairo_bool_t foreground_marker_used;
+    cairo_bool_t foreground_colors_used;
+
 } cairo_user_scaled_font_t;
 
 /* #cairo_user_scaled_font_t */
 
 static cairo_surface_t *
-_cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t *scaled_font,
-						  cairo_bool_t                    color)
+_cairo_user_scaled_font_create_recording_surface (cairo_user_scaled_font_t *scaled_font,
+						  cairo_bool_t              color,
+						  const cairo_color_t      *foreground_color)
 {
     cairo_content_t content;
 
@@ -113,10 +119,20 @@ _cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t
 						         CAIRO_CONTENT_ALPHA;
     }
 
+    if (scaled_font->foreground_pattern)
+	cairo_pattern_destroy (scaled_font->foreground_pattern);
+
+    scaled_font->foreground_marker_used = FALSE;
+    scaled_font->foreground_colors_used = FALSE;
+    if (foreground_color) {
+	scaled_font->foreground_pattern = _cairo_pattern_create_solid (foreground_color);
+    } else {
+	scaled_font->foreground_pattern = cairo_pattern_create_rgb (0, 0, 0);
+    }
+
     return cairo_recording_surface_create (content, NULL);
 }
 
-
 static cairo_t *
 _cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t *scaled_font,
 						  cairo_surface_t                *recording_surface,
@@ -143,7 +159,8 @@ _cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t
 
 static cairo_int_status_t
 _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_font,
-					    cairo_scaled_glyph_t     *scaled_glyph)
+					    cairo_scaled_glyph_t     *scaled_glyph,
+					    const cairo_color_t      *foreground_color)
 {
     cairo_user_font_face_t *face =
 	(cairo_user_font_face_t *) scaled_font->base.font_face;
@@ -151,29 +168,25 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon
     cairo_surface_t *recording_surface = NULL;
     cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
     cairo_t *cr;
+    cairo_bool_t foreground_used = FALSE;
 
     if (!face->scaled_font_methods.render_color_glyph && !face->scaled_font_methods.render_glyph)
 	return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
 
     /* special case for 0 rank matrix (as in _cairo_scaled_font_init): empty surface */
     if (_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) {
-	recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE);
+	recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE, foreground_color);
 	_cairo_scaled_glyph_set_recording_surface (scaled_glyph,
 						   &scaled_font->base,
-						   recording_surface);
+						   recording_surface,
+						   NULL);
     } else {
 	status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
 
 	if (face->scaled_font_methods.render_color_glyph) {
-	    cairo_pattern_t *pattern;
-
-	    recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, TRUE);
+	    recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, TRUE, foreground_color);
 
 	    cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface, TRUE);
-	    pattern = cairo_pattern_create_rgb (0, 0, 0);
-	    pattern->is_userfont_foreground = TRUE;
-	    cairo_set_source (cr, pattern);
-	    cairo_pattern_destroy (pattern);
 	    status = face->scaled_font_methods.render_color_glyph ((cairo_scaled_font_t *)scaled_font,
 								   _cairo_scaled_glyph_index(scaled_glyph),
 								   cr, &extents);
@@ -182,14 +195,16 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon
 		scaled_glyph->color_glyph = TRUE;
 		scaled_glyph->color_glyph_set = TRUE;
 	    }
+
 	    cairo_destroy (cr);
+	    foreground_used = scaled_font->foreground_marker_used || scaled_font->foreground_colors_used;
 	}
 
 	if (status == (cairo_int_status_t)CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED &&
 	    face->scaled_font_methods.render_glyph) {
 	    if (recording_surface)
 		cairo_surface_destroy (recording_surface);
-	    recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE);
+	    recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE, foreground_color);
 	    recording_surface->device_transform.x0 = .25 * _cairo_scaled_glyph_xphase (scaled_glyph);
 	    recording_surface->device_transform.y0 = .25 * _cairo_scaled_glyph_yphase (scaled_glyph);
 
@@ -205,6 +220,7 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon
 	    }
 
 	    cairo_destroy (cr);
+	    foreground_used = FALSE;
 	}
 
 	if (status != CAIRO_INT_STATUS_SUCCESS) {
@@ -215,7 +231,8 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon
 
 	_cairo_scaled_glyph_set_recording_surface (scaled_glyph,
 						   &scaled_font->base,
-						   recording_surface);
+						   recording_surface,
+						   foreground_used ? foreground_color : NULL);
     }
 
     /* set metrics */
@@ -265,8 +282,8 @@ _cairo_user_scaled_glyph_init_surface (cairo_user_scaled_font_t  *scaled_font,
     cairo_surface_t *surface;
     cairo_format_t format;
     int width, height;
-    cairo_bool_t foreground_used;
     cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_bool_t foreground_used;
 
     /* TODO
      * extend the glyph cache to support argb glyphs.
@@ -313,20 +330,23 @@ _cairo_user_scaled_glyph_init_surface (cairo_user_scaled_font_t  *scaled_font,
 									surface,
 									foreground_color,
 									&foreground_used);
+	
     } else {
 	status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, surface);
+	foreground_used = FALSE;
     }
-
     if (unlikely (status)) {
 	cairo_surface_destroy(surface);
 	return status;
     }
 
+    foreground_used = foreground_used || scaled_glyph->recording_uses_foreground_color;
+    
     if (info == CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) {
 	_cairo_scaled_glyph_set_color_surface (scaled_glyph,
 					       &scaled_font->base,
 					       (cairo_image_surface_t *)surface,
-					       foreground_used);
+					       foreground_used ? foreground_color : NULL);
 	surface = NULL;
     } else {
 	_cairo_scaled_glyph_set_surface (scaled_glyph,
@@ -341,6 +361,18 @@ _cairo_user_scaled_glyph_init_surface (cairo_user_scaled_font_t  *scaled_font,
     return status;
 }
 
+static void
+_cairo_user_scaled_glyph_fini (void			 *abstract_font)
+{
+    cairo_user_scaled_font_t *scaled_font = abstract_font;
+
+    if (scaled_font->foreground_pattern)
+	cairo_pattern_destroy (scaled_font->foreground_pattern);
+
+    if (scaled_font->foreground_marker)
+	cairo_pattern_destroy (scaled_font->foreground_marker);
+}
+
 static cairo_int_status_t
 _cairo_user_scaled_glyph_init (void			 *abstract_font,
 			       cairo_scaled_glyph_t	 *scaled_glyph,
@@ -349,9 +381,21 @@ _cairo_user_scaled_glyph_init (void			 *abstract_font,
 {
     cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
     cairo_user_scaled_font_t *scaled_font = abstract_font;
+    cairo_bool_t need_recording = FALSE;
 
     if (!scaled_glyph->recording_surface) {
-	status = _cairo_user_scaled_glyph_init_record_glyph (scaled_font, scaled_glyph);
+	need_recording = TRUE;
+    } else {
+	if ((info & (CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE|CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) &&
+	    scaled_glyph->recording_uses_foreground_color &&
+	    !_cairo_color_equal (foreground_color, &scaled_glyph->foreground_color))
+	{
+	    need_recording = TRUE;
+	}
+    }
+
+    if (need_recording) {
+	status = _cairo_user_scaled_glyph_init_record_glyph (scaled_font, scaled_glyph, foreground_color);
 	if (status)
 	    return status;
     }
@@ -511,7 +555,7 @@ _cairo_user_font_face_create_for_toy (cairo_toy_font_face_t   *toy_face,
 
 static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = {
     CAIRO_FONT_TYPE_USER,
-    NULL,	/* scaled_font_fini */
+    _cairo_user_scaled_glyph_fini,
     _cairo_user_scaled_glyph_init,
     _cairo_user_text_to_glyphs,
     _cairo_user_ucs4_to_index,
@@ -553,6 +597,10 @@ _cairo_user_font_face_scaled_font_create (void                        *abstract_
 	return status;
     }
 
+    user_scaled_font->foreground_pattern = NULL;
+    user_scaled_font->foreground_marker = cairo_pattern_create_rgb (0, 0, 0);
+    user_scaled_font->foreground_marker->is_userfont_foreground = TRUE;
+
     /* XXX metrics hinting? */
 
     /* compute a normalized version of font scale matrix to compute
@@ -601,7 +649,7 @@ _cairo_user_font_face_scaled_font_create (void                        *abstract_
 	    cairo_surface_t *recording_surface;
 	    cairo_t *cr;
 
-	    recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font, FALSE);
+	    recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font, FALSE, NULL);
 	    cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font, recording_surface, FALSE);
 	    cairo_surface_destroy (recording_surface);
 
@@ -1056,3 +1104,119 @@ cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face)
     user_font_face = (cairo_user_font_face_t *) font_face;
     return user_font_face->scaled_font_methods.unicode_to_glyph;
 }
+
+/**
+ * cairo_user_scaled_font_get_foreground_marker:
+ * @scaled_font: A user scaled font
+ *
+ * Gets the foreground pattern of the glyph currently being
+ * rendered. A #cairo_user_scaled_font_render_glyph_func_t function
+ * that has been set with
+ * cairo_user_font_face_set_render_color_glyph_func() may call this
+ * function to retrieve the current foreground pattern for the glyph
+ * being rendered. The function should not be called outside of a
+ * cairo_user_font_face_set_render_color_glyph_func() callback.
+ *
+ * The foreground marker pattern contains an internal marker to
+ * indicate that it is to be substituted with the current source when
+ * rendered to a surface. Querying the foreground marker will reveal a
+ * solid black color, however this is not representative of the color
+ * that will actually be used. Similarly, setting a solid black color
+ * will render black, not the foreground pattern when the glyph is
+ * painted to a surface. Using the foreground marker as the source
+ * instead of cairo_user_scaled_font_get_foreground_source() in a
+ * color render callback has the following benefits:
+ *
+ * 1. Cairo only needs to call the render callback once as it can
+ * cache the recording. Cairo will substitute the actual foreground
+ * color when rendering the recording.
+ *
+ * 2. On backends that have the concept of a foreground color in fonts such as
+ * PDF, PostScript, and SVG, cairo can generate more optimal
+ * output. The glyph can be included in an embedded font.
+ *
+ * The one drawback of the using foreground marker is the render
+ * callback can not access the color components of the pattern as the
+ * actual foreground pattern is not available at the time the render
+ * callback is invoked. If the render callback needs to query the
+ * foreground pattern, use
+ * cairo_user_scaled_font_get_foreground_source().
+ *
+ * If the render callback simply wants to call cairo_set_source() with
+ * the foreground pattern,
+ * cairo_user_scaled_font_get_foreground_marker() is the preferred
+ * function to use as it results in better performance than
+ * cairo_user_scaled_font_get_foreground_source().
+ *
+ * Return value: the current foreground source marker pattern. This
+ * object is owned by cairo. This object must not be modified or used
+ * outside of a color render callback. To keep a reference to it,
+ * you must call cairo_pattern_reference().
+ *
+ * Since: 1.18
+ **/
+cairo_pattern_t *
+cairo_user_scaled_font_get_foreground_marker (cairo_scaled_font_t *scaled_font)
+{
+    cairo_user_scaled_font_t *user_scaled_font;
+
+    if (scaled_font->backend != &_cairo_user_scaled_font_backend)
+	return _cairo_pattern_create_in_error (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+
+    user_scaled_font = (cairo_user_scaled_font_t *)scaled_font;
+    return user_scaled_font->foreground_marker;
+}
+
+/**
+ * cairo_user_scaled_font_get_foreground_source:
+ * @scaled_font: A user scaled font
+ *
+ * Gets the foreground pattern of the glyph currently being
+ * rendered. A #cairo_user_scaled_font_render_glyph_func_t function
+ * that has been set with
+ * cairo_user_font_face_set_render_color_glyph_func() may call this
+ * function to retrieve the current foreground pattern for the glyph
+ * being rendered. The function should not be called outside of a
+ * cairo_user_font_face_set_render_color_glyph_func() callback.
+ *
+ * This function returns the current source at the time the glyph is
+ * rendered. Compared with
+ * cairo_user_scaled_font_get_foreground_marker(), this function
+ * returns the actual source pattern that will be used to render the
+ * glyph.  The render callback is free to query the pattern and
+ * extract color components or other pattern data. For example if the
+ * render callback wants to create a gradient stop based on colors in
+ * the foreground source pattern, it will need to use this function in
+ * order to be able to query the colors in the foreground pattern.
+ *
+ * While this function does not have the restrictions on using the
+ * pattern that cairo_user_scaled_font_get_foreground_marker() has, it
+ * does incur a performance penalty. If a render callback calls this
+ * function:
+ *
+ * 1. Cairo will call the render callback whenever the current pattern
+ * of the context in which the glyph is rendered changes.
+ *
+ * 2. On backends that support font embedding (PDF, PostScript, and
+ * SVG), cairo can not embed this glyph in a font. Instead the glyph
+ * will be emitted as an image or sequence of drawing operations each
+ * time it is used.
+ *
+ * Return value: the current foreground source pattern. This object is
+ * owned by cairo. To keep a reference to it, you must call
+ * cairo_pattern_reference().
+ *
+ * Since: 1.18
+ **/
+cairo_pattern_t *
+cairo_user_scaled_font_get_foreground_source (cairo_scaled_font_t *scaled_font)
+{
+    cairo_user_scaled_font_t *user_scaled_font;
+
+    if (scaled_font->backend != &_cairo_user_scaled_font_backend)
+	return _cairo_pattern_create_in_error (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+
+    user_scaled_font = (cairo_user_scaled_font_t *)scaled_font;
+    user_scaled_font->foreground_colors_used = TRUE;
+    return user_scaled_font->foreground_pattern;
+}
diff --git a/src/cairo.h b/src/cairo.h
index c74d7c2c5..a9b423b28 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1815,25 +1815,14 @@ typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_
  * cairo_user_font_face_set_render_glyph_func(), the result is
  * undefined if any source other than the default source on @cr is
  * used.  That means, glyph bitmaps should be rendered using
- * cairo_mask() instead of cairo_paint(). When this callback is set with
- * cairo_user_font_face_set_render_color_glyph_func(), setting the
- * source is a valid operation.
+ * cairo_mask() instead of cairo_paint().
  *
  * When this callback is set with
  * cairo_user_font_face_set_render_color_glyph_func(), the default
- * source is the current source color of the context that is rendering
- * the user font. That is, the same color a non-color user font will
- * be rendered in. In most cases the callback will want to set a
- * specific color. If the callback wishes to use the current context
- * color after using another source, it should retain a reference to
- * the source or use cairo_save()/cairo_restore() prior to changing
- * the source. Note that the default source contains an internal
- * marker to indicate that it is to be substituted with the current
- * context source color when rendered to a surface. Querying the
- * default source pattern will reveal a solid black color, however
- * this is not representative of the color that will actually be
- * used. Similarly, setting a solid black color will render black, not
- * the current context source when the glyph is painted to a surface.
+ * source is black. Setting the source is a valid
+ * operation. cairo_user_scaled_font_get_foreground_marker() or
+ * cairo_user_scaled_font_get_foreground_source() may be called to
+ * obtain the current source at the time the glyph is rendered.
  *
  * Other non-default settings on @cr include a font size of 1.0 (given that
  * it is set up to be in font space), and font options corresponding to
@@ -1856,10 +1845,13 @@ typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_
  * Where both color and non-color callbacks has been set using
  * cairo_user_font_face_set_render_color_glyph_func(), and
  * cairo_user_font_face_set_render_glyph_func(), the color glyph
- * callback may return %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if the
- * glyph is not a color glyph. This is the only case in which the
- * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED may be returned from a
- * render callback.
+ * callback will be called first. If the color glyph callback returns
+ * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, any drawing operations are
+ * discarded and the non-color callback will be called. This is the
+ * only case in which the %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED may
+ * be returned from a render callback. This fallback sequence allows a
+ * user font face to contain a combination of both color and non-color
+ * glyphs.
  *
  * Returns: %CAIRO_STATUS_SUCCESS upon success,
  * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried,
@@ -2027,6 +2019,11 @@ cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face);
 cairo_public cairo_user_scaled_font_unicode_to_glyph_func_t
 cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face);
 
+cairo_public cairo_pattern_t *
+cairo_user_scaled_font_get_foreground_marker (cairo_scaled_font_t *scaled_font);
+
+cairo_public cairo_pattern_t *
+cairo_user_scaled_font_get_foreground_source (cairo_scaled_font_t *scaled_font);
 
 /* Query functions */
 
diff --git a/src/cairoint.h b/src/cairoint.h
index 65bc16f53..af2501057 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1292,13 +1292,14 @@ _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph,
 cairo_private void
 _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
                                            cairo_scaled_font_t *scaled_font,
-                                           cairo_surface_t *recording_surface);
+                                           cairo_surface_t *recording_surface,
+					   const cairo_color_t *foreground_color);
 
 cairo_private void
 _cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph,
 		                       cairo_scaled_font_t *scaled_font,
 		                       cairo_image_surface_t *surface,
-                                       cairo_bool_t uses_foreground_color);
+                                       const cairo_color_t *foreground_color);
 
 cairo_private cairo_int_status_t
 _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp
index 764835bc8..f0657d636 100644
--- a/src/win32/cairo-dwrite-font.cpp
+++ b/src/win32/cairo-dwrite-font.cpp
@@ -1065,7 +1065,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s
     _cairo_scaled_glyph_set_color_surface (scaled_glyph,
 					   &scaled_font->base,
 					   (cairo_image_surface_t *) image,
-					   uses_foreground_color);
+					   uses_foreground_color ? foreground_color : NULL);
     scaled_glyph->color_glyph = TRUE;
     scaled_glyph->color_glyph_set = TRUE;
 


More information about the cairo-commit mailing list