[cairo-commit] 10 commits - boilerplate/cairo-boilerplate.c doc/public src/cairoint.h src/cairo-pattern.c src/cairo-ps.h src/cairo-ps-surface.c src/cairo-ps-surface-private.h src/cairo-scaled-font-subsets.c src/cairo-scaled-font-subsets-private.h src/cairo-type1-fallback.c src/cairo-win32-printing-surface.c test/clip-fill-rule-ps-rgb24-ref.png test/clip-nesting-ps-rgb24-ref.png test/clip-operator-ps-argb32-ref.png test/clip-push-group-ps-argb32-ref.png test/clip-push-group-ps-rgb24-ref.png test/clip-twice-ps-rgb24-ref.png test/copy-path-ps-rgb24-ref.png test/dash-caps-joins-ps-rgb24-ref.png test/dash-scale-ps-rgb24-ref.png test/dash-state-ps-rgb24-ref.png test/degenerate-path-ps-rgb24-ref.png test/fill-and-stroke-ps-rgb24-ref.png test/fill-missed-stop-ps-rgb24-ref.png test/fill-rule-ps-rgb24-ref.png test/font-matrix-translation-ps-rgb24-ref.png test/.gitignore test/gradient-alpha-ps-argb32-ref.png test/gradient-alpha-ps-rgb24-ref.png test/leaky-dash-ps-rgb24-ref.png test/linear-gradient-ps-argb32-ref.png test/linear-gradient-ps-rgb24-ref.png test/line-width-scale-ps-rgb24-ref.png test/Makefile.am test/meta-surface-pattern.c test/meta-surface-pattern-ref.png test/meta-surface-pattern-rgb24-ref.png test/new-sub-path-ps-rgb24-ref.png test/operator-source-ps-argb32-ref.png test/operator-source-ps-rgb24-ref.png test/pixman-rotate-ps-argb32-ref.png test/pixman-rotate-ps-rgb24-ref.png test/random-intersections-ps-rgb24-ref.png test/rel-path-ps-rgb24-ref.png test/rotate-image-surface-paint-ps-rgb24-ref.png test/source-clip-scale-ps-argb32-ref.png test/source-clip-scale-ps-rgb24-ref.png test/text-pattern-ps-argb32-ref.png test/text-pattern-ps-rgb24-ref.png test/text-rotate-ps-rgb24-ref.png test/trap-clip-ps-argb32-ref.png test/trap-clip-ps-rgb24-ref.png

Adrian Johnson ajohnson at kemper.freedesktop.org
Sat Oct 13 07:55:22 PDT 2007


 boilerplate/cairo-boilerplate.c                  |   11 
 doc/public/cairo-sections.txt                    |    4 
 src/cairo-pattern.c                              |    2 
 src/cairo-ps-surface-private.h                   |    4 
 src/cairo-ps-surface.c                           | 1186 ++++++++++++++++++-----
 src/cairo-ps.h                                   |   25 
 src/cairo-scaled-font-subsets-private.h          |   15 
 src/cairo-scaled-font-subsets.c                  |  159 +++
 src/cairo-type1-fallback.c                       |   18 
 src/cairo-win32-printing-surface.c               |    6 
 src/cairoint.h                                   |    1 
 test/.gitignore                                  |    1 
 test/Makefile.am                                 |    1 
 test/clip-fill-rule-ps-rgb24-ref.png             |binary
 test/clip-nesting-ps-rgb24-ref.png               |binary
 test/clip-operator-ps-argb32-ref.png             |binary
 test/clip-push-group-ps-argb32-ref.png           |binary
 test/clip-push-group-ps-rgb24-ref.png            |binary
 test/clip-twice-ps-rgb24-ref.png                 |binary
 test/copy-path-ps-rgb24-ref.png                  |binary
 test/dash-caps-joins-ps-rgb24-ref.png            |binary
 test/dash-scale-ps-rgb24-ref.png                 |binary
 test/dash-state-ps-rgb24-ref.png                 |binary
 test/degenerate-path-ps-rgb24-ref.png            |binary
 test/fill-and-stroke-ps-rgb24-ref.png            |binary
 test/fill-missed-stop-ps-rgb24-ref.png           |binary
 test/fill-rule-ps-rgb24-ref.png                  |binary
 test/font-matrix-translation-ps-rgb24-ref.png    |binary
 test/gradient-alpha-ps-argb32-ref.png            |binary
 test/gradient-alpha-ps-rgb24-ref.png             |binary
 test/leaky-dash-ps-rgb24-ref.png                 |binary
 test/line-width-scale-ps-rgb24-ref.png           |binary
 test/linear-gradient-ps-argb32-ref.png           |binary
 test/linear-gradient-ps-rgb24-ref.png            |binary
 test/meta-surface-pattern-ref.png                |binary
 test/meta-surface-pattern-rgb24-ref.png          |binary
 test/meta-surface-pattern.c                      |  131 ++
 test/new-sub-path-ps-rgb24-ref.png               |binary
 test/operator-source-ps-argb32-ref.png           |binary
 test/operator-source-ps-rgb24-ref.png            |binary
 test/pixman-rotate-ps-argb32-ref.png             |binary
 test/pixman-rotate-ps-rgb24-ref.png              |binary
 test/random-intersections-ps-rgb24-ref.png       |binary
 test/rel-path-ps-rgb24-ref.png                   |binary
 test/rotate-image-surface-paint-ps-rgb24-ref.png |binary
 test/source-clip-scale-ps-argb32-ref.png         |binary
 test/source-clip-scale-ps-rgb24-ref.png          |binary
 test/text-pattern-ps-argb32-ref.png              |binary
 test/text-pattern-ps-rgb24-ref.png               |binary
 test/text-rotate-ps-rgb24-ref.png                |binary
 test/trap-clip-ps-argb32-ref.png                 |binary
 test/trap-clip-ps-rgb24-ref.png                  |binary
 52 files changed, 1312 insertions(+), 252 deletions(-)

New commits:
diff-tree 5af1b2280b2cf952624d587496158a98df0ee504 (from e347a7a7c394fc2638faa2ff52e4b96545ac1ee3)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Oct 14 00:04:21 2007 +0930

    Make PostScript output text selectable
    
    The glyph names used in the Type42 and Type1 fallback fonts are now of
    the form "/uniXXXX" where XXXX is the unicode character for each
    glyph. When converted to pdf (eg using ps2pdf), pdf viewers are now
    able to correctly extract the text.

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 806c244..013f025 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -508,18 +508,32 @@ _cairo_ps_surface_emit_truetype_font_sub
 
     /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */
 
-    for (i = 1; i < font_subset->num_glyphs; i++)
-	_cairo_output_stream_printf (surface->final_stream,
-				     "Encoding %d /g%d put\n", i, i);
+    for (i = 1; i < font_subset->num_glyphs; i++) {
+	if (font_subset->glyph_names != NULL) {
+	    _cairo_output_stream_printf (surface->final_stream,
+					 "Encoding %d /%s put\n",
+					 i, font_subset->glyph_names[i]);
+	} else {
+	    _cairo_output_stream_printf (surface->final_stream,
+					 "Encoding %d /g%d put\n", i, i);
+	}
+    }
 
     _cairo_output_stream_printf (surface->final_stream,
 				 "/CharStrings %d dict dup begin\n"
 				 "/.notdef 0 def\n",
 				 font_subset->num_glyphs);
 
-    for (i = 1; i < font_subset->num_glyphs; i++)
-	_cairo_output_stream_printf (surface->final_stream,
-				     "/g%d %d def\n", i, i);
+    for (i = 1; i < font_subset->num_glyphs; i++) {
+	if (font_subset->glyph_names != NULL) {
+	    _cairo_output_stream_printf (surface->final_stream,
+					 "/%s %d def\n",
+					 font_subset->glyph_names[i], i);
+	} else {
+	    _cairo_output_stream_printf (surface->final_stream,
+					 "/g%d %d def\n", i, i);
+	}
+    }
 
     _cairo_output_stream_printf (surface->final_stream,
 				 "end readonly def\n");
@@ -742,7 +756,6 @@ _cairo_ps_surface_emit_type3_font_subset
     return CAIRO_STATUS_SUCCESS;
 }
 
-
 static cairo_status_t
 _cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t	*font_subset,
 				            void			*closure)
@@ -750,6 +763,11 @@ _cairo_ps_surface_emit_unscaled_font_sub
     cairo_ps_surface_t *surface = closure;
     cairo_status_t status;
 
+
+    status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
+    if (status && status != CAIRO_INT_STATUS_UNSUPPORTED)
+	return status;
+
 #if CAIRO_HAS_FT_FONT
     status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset);
     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
@@ -774,6 +792,10 @@ _cairo_ps_surface_emit_scaled_font_subse
     cairo_ps_surface_t *surface = closure;
     cairo_status_t status;
 
+    status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
+    if (status && status != CAIRO_INT_STATUS_UNSUPPORTED)
+	return status;
+
     status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset);
     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	return status;
diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h
index d6dcb3f..f80257b 100644
--- a/src/cairo-scaled-font-subsets-private.h
+++ b/src/cairo-scaled-font-subsets-private.h
@@ -262,6 +262,21 @@ _cairo_scaled_font_subsets_foreach_unsca
                                              cairo_scaled_font_subset_callback_func_t  font_subset_callback,
 				             void				      *closure);
 
+/**
+ * _cairo_scaled_font_subset_create_glyph_names:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ *
+ * Create an array of strings containing the glyph name for each glyph
+ * in @font_subsets. The array as store in font_subsets->glyph_names.
+ *
+ * Return value: CAIRO_STATUS_SUCCESS if successful,
+ * CAIRO_INT_STATUS_UNSUPPORTED if the font backend does not support
+ * mapping the glyph indices to unicode characters. Possible errors
+ * include CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_int_status_t
+_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset);
+
 typedef struct _cairo_cff_subset {
     char *base_font;
     int *widths;
diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
index d9795eb..320822b 100644
--- a/src/cairo-scaled-font-subsets.c
+++ b/src/cairo-scaled-font-subsets.c
@@ -37,6 +37,7 @@
  *	Carl D. Worth <cworth at cworth.org>
  *	Kristian Høgsberg <krh at redhat.com>
  *	Keith Packard <keithp at keithp.com>
+ *	Adrian Johnson <ajohnson at redneon.com>
  */
 
 #include "cairoint.h"
@@ -100,6 +101,11 @@ typedef struct _cairo_sub_font_collectio
     void *font_subset_callback_closure;
 } cairo_sub_font_collection_t;
 
+typedef struct _cairo_string_entry {
+    cairo_hash_entry_t base;
+    char *string;
+} cairo_string_entry_t;
+
 static void
 _cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t  *sub_font_glyph,
 				unsigned long		 scaled_font_glyph_index)
@@ -402,6 +408,7 @@ _cairo_sub_font_collect (void *entry, vo
 	subset.subset_id = i;
 	subset.glyphs = collection->glyphs;
 	subset.num_glyphs = collection->num_glyphs;
+        subset.glyph_names = NULL;
         /* No need to check for out of memory here. If to_unicode is NULL, the PDF
          * surface does not emit an ToUnicode stream */
         subset.to_unicode = _cairo_malloc_ab (collection->num_glyphs, sizeof (unsigned long));
@@ -417,6 +424,12 @@ _cairo_sub_font_collect (void *entry, vo
         if (subset.to_unicode != NULL)
             free (subset.to_unicode);
 
+	if (subset.glyph_names != NULL) {
+            for (j = 0; j < collection->num_glyphs; j++)
+		free (subset.glyph_names[j]);
+	    free (subset.glyph_names);
+	}
+
 	if (collection->status)
 	    break;
     }
@@ -686,3 +699,149 @@ _cairo_scaled_font_subsets_foreach_unsca
                                                         closure,
                                                         FALSE);
 }
+
+static cairo_bool_t
+_cairo_string_equal (const void *key_a, const void *key_b)
+{
+    const cairo_string_entry_t *a = key_a;
+    const cairo_string_entry_t *b = key_b;
+
+    if (strcmp (a->string, b->string) == 0)
+	return TRUE;
+    else
+	return FALSE;
+}
+
+static void
+_cairo_string_init_key (cairo_string_entry_t *key, char *s)
+{
+    unsigned long sum = 0;
+    unsigned int i;
+
+    for (i = 0; i < strlen(s); i++)
+        sum += s[i];
+    key->base.hash = sum;
+    key->string = s;
+}
+
+static cairo_string_entry_t *
+create_string_entry (char *s)
+{
+    cairo_string_entry_t *entry;
+
+    entry = malloc (sizeof (cairo_string_entry_t));
+    if (entry == NULL) {
+	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+        return NULL;
+    }
+
+    _cairo_string_init_key (entry, s);
+
+    return entry;
+}
+
+cairo_int_status_t
+_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset)
+{
+    const cairo_scaled_font_backend_t *backend;
+    unsigned int i;
+    cairo_status_t status;
+    cairo_hash_table_t *names;
+    cairo_string_entry_t key, *entry;
+    char buf[30];
+
+    if (subset->to_unicode == NULL) {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (_cairo_truetype_create_glyph_to_unicode_map (subset) != CAIRO_STATUS_SUCCESS) {
+        backend = subset->scaled_font->backend;
+        if (backend->map_glyphs_to_unicode == NULL) {
+            return CAIRO_INT_STATUS_UNSUPPORTED;
+        }
+        backend->map_glyphs_to_unicode (subset->scaled_font, subset);
+    }
+
+    subset->glyph_names = calloc (subset->num_glyphs, sizeof (char *));
+
+    names = _cairo_hash_table_create (_cairo_string_equal);
+    if (names == NULL) {
+	status = CAIRO_STATUS_NO_MEMORY;
+	goto FAIL1;
+    }
+
+    subset->glyph_names[0] = strdup (".notdef");
+    if (subset->glyph_names[0] == NULL) {
+	status = CAIRO_STATUS_NO_MEMORY;
+	goto FAIL1;
+    }
+
+    entry = create_string_entry (subset->glyph_names[0]);
+    if (entry == NULL) {
+	status = CAIRO_STATUS_NO_MEMORY;
+	goto FAIL2;
+    }
+
+    status = _cairo_hash_table_insert (names, &entry->base);
+    if (status) {
+	free (entry);
+	goto CLEANUP_HASH;
+    }
+
+    for (i = 0; i < subset->num_glyphs; i++) {
+	if (subset->to_unicode[i] <= 0xffff) {
+	    snprintf (buf, sizeof(buf), "uni%04X", (unsigned int)(subset->to_unicode[i]));
+	    _cairo_string_init_key (&key, buf);
+	    if (_cairo_hash_table_lookup (names, &key.base,
+					  (cairo_hash_entry_t **) &entry)) {
+		snprintf (buf, sizeof(buf), "g%d", i);
+	    }
+	} else {
+	    snprintf (buf, sizeof(buf), "g%d", i);
+	}
+
+	subset->glyph_names[i] = strdup (buf);
+	if (subset->glyph_names[i] == NULL) {
+	    status = CAIRO_STATUS_NO_MEMORY;
+	    goto CLEANUP_HASH;
+	}
+
+	entry = create_string_entry (subset->glyph_names[i]);
+	if (entry == NULL) {
+	    status = CAIRO_STATUS_NO_MEMORY;
+	    goto CLEANUP_HASH;
+	}
+
+	status = _cairo_hash_table_insert (names, &entry->base);
+	if (status) {
+	    free (entry);
+	    goto CLEANUP_HASH;
+	}
+    }
+    return 0;
+
+CLEANUP_HASH:
+    while (1) {
+	entry = _cairo_hash_table_random_entry (names, NULL);
+	if (entry == NULL)
+	    break;
+        _cairo_hash_table_remove (names, (cairo_hash_entry_t *) entry);
+        free (entry);
+    }
+    _cairo_hash_table_destroy (names);
+
+    if (status == CAIRO_STATUS_SUCCESS)
+	return status;
+
+FAIL2:
+    for (i = 0; i < subset->num_glyphs; i++) {
+	if (subset->glyph_names[i] != NULL)
+	    free (subset->glyph_names[i]);
+    }
+
+FAIL1:
+    free (subset->glyph_names);
+    subset->glyph_names = NULL;
+
+    return status;
+}
diff --git a/src/cairo-type1-fallback.c b/src/cairo-type1-fallback.c
index 37aa22c..626c7b5 100644
--- a/src/cairo-type1-fallback.c
+++ b/src/cairo-type1-fallback.c
@@ -465,7 +465,13 @@ cairo_type1_font_write_charstrings (cair
             goto fail;
         charstring_encrypt (&data);
         length = _cairo_array_num_elements (&data);
-        _cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length);
+	if (font->scaled_font_subset->glyph_names != NULL) {
+	    _cairo_output_stream_printf (encrypted_output, "/%s %d RD ",
+					 font->scaled_font_subset->glyph_names[i],
+					 length);
+	} else {
+	    _cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length);
+	}
         _cairo_output_stream_write (encrypted_output,
                                     _cairo_array_index (&data, 0),
                                     length);
@@ -527,8 +533,14 @@ cairo_type1_font_write_header (cairo_typ
                                  "} readonly def\n"
                                  "/Encoding 256 array\n"
 				 "0 1 255 {1 index exch /.notdef put} for\n");
-    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
-        _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i);
+    for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
+	if (font->scaled_font_subset->glyph_names != NULL) {
+	    _cairo_output_stream_printf (font->output, "dup %d /%s put\n",
+					 i, font->scaled_font_subset->glyph_names[i]);
+	} else {
+	    _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i);
+	}
+    }
     _cairo_output_stream_printf (font->output,
                                  "readonly def\n"
                                  "currentdict end\n"
diff --git a/src/cairoint.h b/src/cairoint.h
index caabef4..16e8a08 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -459,6 +459,7 @@ typedef struct _cairo_scaled_font_subset
      */
     unsigned long *glyphs;
     unsigned long *to_unicode;
+    char          **glyph_names;
     unsigned int num_glyphs;
     cairo_bool_t is_composite;
 } cairo_scaled_font_subset_t;
diff-tree e347a7a7c394fc2638faa2ff52e4b96545ac1ee3 (from af5cdde34d003b7d5943b169c160b91b8f7d10f3)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Oct 13 22:59:20 2007 +0930

    PS: Add support for images with bilevel alpha
    
    This patch eliminates two sources of fallback images. The alpha value
    of each pixel in argb32 images are checked. If all alpha values are
    255 the image is treated the same as rgb24 images. If all alpha values
    are either 0 or 255 and the PS level is 3, a Type 3 image (image +
    mask) is emitted.

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 0e4e5bc..806c244 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -2,6 +2,7 @@
  *
  * Copyright © 2003 University of Southern California
  * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2007 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
@@ -35,6 +36,7 @@
  *	Carl D. Worth <cworth at cworth.org>
  *	Kristian Høgsberg <krh at redhat.com>
  *	Keith Packard <keithp at keithp.com>
+ *	Adrian Johnson <ajohnson at redneon.com>
  */
 
 #include "cairoint.h"
@@ -51,6 +53,12 @@
 
 #define DEBUG_PS 0
 
+typedef enum _cairo_image_transparency {
+    CAIRO_IMAGE_IS_OPAQUE,
+    CAIRO_IMAGE_HAS_BILEVEL_ALPHA,
+    CAIRO_IMAGE_HAS_ALPHA
+} cairo_image_transparency_t;
+
 static const cairo_surface_backend_t cairo_ps_surface_backend;
 static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend;
 
@@ -1481,6 +1489,92 @@ color_is_gray (double red, double green,
 	    fabs (red - blue) < epsilon);
 }
 
+static cairo_status_t
+_analyze_image_transparency (cairo_image_surface_t      *image,
+			     cairo_image_transparency_t *transparency)
+{
+    cairo_status_t status;
+    int x, y;
+
+    if (image->format == CAIRO_FORMAT_RGB24) {
+	*transparency = CAIRO_IMAGE_IS_OPAQUE;
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (image->format != CAIRO_FORMAT_ARGB32) {
+	/* If the PS surface does not support the image format, assume
+	 * that it does have alpha. The image will be converted to
+	 * rgb24 when the PS surface blends the image into the page
+	 * color to remove the transparency. */
+	*transparency = CAIRO_IMAGE_HAS_ALPHA;
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    *transparency = CAIRO_IMAGE_IS_OPAQUE;
+    for (y = 0; y < image->height; y++) {
+	int a;
+	uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
+
+	for (x = 0; x < image->width; x++, pixel++) {
+	    a = (*pixel & 0xff000000) >> 24;
+	    if (a > 0 && a < 255) {
+		*transparency = CAIRO_IMAGE_HAS_ALPHA;
+		return CAIRO_STATUS_SUCCESS;
+	    } else if (a == 0) {
+		*transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+	    }
+	}
+    }
+    status = CAIRO_STATUS_SUCCESS;
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t      *surface,
+						       cairo_surface_pattern_t *pattern)
+{
+    cairo_image_surface_t  *image;
+    void		   *image_extra;
+    cairo_int_status_t      status;
+    cairo_image_transparency_t transparency;
+
+    status = _cairo_surface_acquire_source_image (pattern->surface,
+						  &image,
+						  &image_extra);
+    if (status)
+	return status;
+
+    if (image->base.status)
+	return image->base.status;
+
+    status = _analyze_image_transparency (image, &transparency);
+    if (status)
+	goto RELEASE_SOURCE;
+
+    switch (transparency) {
+    case CAIRO_IMAGE_IS_OPAQUE:
+	status = CAIRO_STATUS_SUCCESS;
+	break;
+
+    case CAIRO_IMAGE_HAS_BILEVEL_ALPHA:
+	if (surface->ps_level == CAIRO_PS_LEVEL_2)
+	    status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+	else
+	    status = CAIRO_STATUS_SUCCESS;
+	break;
+
+    case CAIRO_IMAGE_HAS_ALPHA:
+	status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+	break;
+    }
+
+RELEASE_SOURCE:
+    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+    return status;
+}
+
 static cairo_bool_t
 surface_pattern_supported (cairo_surface_pattern_t *pattern)
 {
@@ -1578,7 +1672,7 @@ pattern_supported (cairo_ps_surface_t *s
 static cairo_int_status_t
 _cairo_ps_surface_analyze_operation (cairo_ps_surface_t    *surface,
 				     cairo_operator_t       op,
-				     cairo_pattern_t *pattern)
+				     cairo_pattern_t       *pattern)
 {
     if (surface->force_fallbacks && surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -1590,16 +1684,19 @@ _cairo_ps_surface_analyze_operation (cai
 	  op == CAIRO_OPERATOR_OVER))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
+    if (op == CAIRO_OPERATOR_SOURCE)
+	return CAIRO_STATUS_SUCCESS;
+
     if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
 	cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
-	if ( _cairo_surface_is_meta (surface_pattern->surface)) {
+
+	if ( _cairo_surface_is_meta (surface_pattern->surface))
 	    return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN;
-	}
+	else
+	    return _cairo_ps_surface_analyze_surface_pattern_transparency (surface,
+									   surface_pattern);
     }
 
-    if (op == CAIRO_OPERATOR_SOURCE)
-	return CAIRO_STATUS_SUCCESS;
-
     /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If
      * the pattern contains transparency, we return
      * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis
@@ -1753,99 +1850,190 @@ _string_array_stream_create (cairo_outpu
  * surface we can render natively in PS. */
 
 static cairo_status_t
-_cairo_ps_surface_emit_image (cairo_ps_surface_t    *surface,
-			      cairo_image_surface_t *image,
-			      const char	    *name)
+_cairo_ps_surface_flatten_image_transparency (cairo_ps_surface_t    *surface,
+					      cairo_image_surface_t *image,
+					      cairo_image_surface_t **opaque_image)
 {
-    cairo_status_t status, status2;
-    unsigned char *rgb, *compressed;
-    unsigned long rgb_size, compressed_size;
+    const cairo_color_t *background_color;
     cairo_surface_t *opaque;
-    cairo_image_surface_t *opaque_image;
     cairo_pattern_union_t pattern;
-    int x, y, i;
+    cairo_status_t status;
+
+    if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
+	background_color = CAIRO_COLOR_WHITE;
+    else
+	background_color = CAIRO_COLOR_BLACK;
+
+    opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+					 image->width,
+					 image->height);
+    if (opaque->status) {
+	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	return status;
+    }
+
+    _cairo_pattern_init_for_surface (&pattern.surface, &image->base);
+
+    status = _cairo_surface_fill_rectangle (opaque,
+					    CAIRO_OPERATOR_SOURCE,
+					    background_color,
+					    0, 0,
+					    image->width, image->height);
+    if (status)
+	goto fail;
+
+    status = _cairo_surface_composite (CAIRO_OPERATOR_OVER,
+				       &pattern.base,
+				       NULL,
+				       opaque,
+				       0, 0,
+				       0, 0,
+				       0, 0,
+				       image->width,
+				       image->height);
+    if (status)
+	goto fail;
+
+    _cairo_pattern_fini (&pattern.base);
+    *opaque_image = (cairo_image_surface_t *) opaque;
+
+    return CAIRO_STATUS_SUCCESS;
+
+fail:
+    _cairo_pattern_fini (&pattern.base);
+    cairo_surface_destroy (opaque);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_base85_string (cairo_ps_surface_t    *surface,
+				      unsigned char 	    *data,
+				      unsigned long 	     length)
+{
     cairo_output_stream_t *base85_stream, *string_array_stream;
+    cairo_status_t status, status2;
 
-    /* PostScript can not represent the alpha channel, so we blend the
-       current image over a white (or black for CONTENT_COLOR
-       surfaces) RGB surface to eliminate it. */
+    string_array_stream = _string_array_stream_create (surface->stream);
+    status = _cairo_output_stream_get_status (string_array_stream);
+    if (status)
+	return status;
 
-    if (image->base.status)
-	return image->base.status;
+    base85_stream = _cairo_base85_stream_create (string_array_stream);
+    status = _cairo_output_stream_get_status (base85_stream);
+    if (status) {
+	status2 = _cairo_output_stream_destroy (string_array_stream);
+	return status;
+    }
 
-    if (image->format != CAIRO_FORMAT_RGB24) {
-	const cairo_color_t *background_color;
+    _cairo_output_stream_write (base85_stream, data, length);
 
-	if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
-	    background_color = CAIRO_COLOR_WHITE;
-	else
-	    background_color = CAIRO_COLOR_BLACK;
+    status = _cairo_output_stream_destroy (base85_stream);
+    status2 = _cairo_output_stream_destroy (string_array_stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+	status = status2;
 
-	opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
-					     image->width,
-					     image->height);
-	if (opaque->status) {
-	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	    goto bail0;
-	}
-	opaque_image = (cairo_image_surface_t *) opaque;
+    return status;
+}
 
-	_cairo_pattern_init_for_surface (&pattern.surface, &image->base);
+static cairo_status_t
+_cairo_ps_surface_emit_image (cairo_ps_surface_t    *surface,
+			      cairo_image_surface_t *image,
+			      const char	    *name,
+			      cairo_operator_t 	     op)
+{
+    cairo_status_t status;
+    unsigned char *rgb, *rgb_compressed;
+    unsigned long rgb_size, rgb_compressed_size;
+    unsigned char *mask = NULL, *mask_compressed = NULL;
+    unsigned long mask_size = 0, mask_compressed_size = 0;
+    cairo_image_surface_t *opaque_image = NULL;
+    int x, y, i;
+    cairo_image_transparency_t transparency;
+    cairo_bool_t use_mask;
 
-	status = _cairo_surface_fill_rectangle (opaque,
-				                CAIRO_OPERATOR_SOURCE,
-						background_color,
-						0, 0,
-					       	image->width, image->height);
-	if (status) {
-	    _cairo_pattern_fini (&pattern.base);
-	    goto bail1;
-	}
+    if (image->base.status)
+	return image->base.status;
 
-	status = _cairo_surface_composite (CAIRO_OPERATOR_OVER,
-				           &pattern.base,
-					   NULL,
-					   opaque,
-					   0, 0,
-					   0, 0,
-					   0, 0,
-					   image->width,
-					   image->height);
-	if (status) {
-	    _cairo_pattern_fini (&pattern.base);
-	    goto bail1;
-	}
+    status = _analyze_image_transparency (image, &transparency);
+    if (status)
+	return status;
 
-	_cairo_pattern_fini (&pattern.base);
-    } else {
-	opaque = &image->base;
+    /* PostScript can not represent the alpha channel, so we blend the
+       current image over a white (or black for CONTENT_COLOR
+       surfaces) RGB surface to eliminate it. */
+
+    if (op == CAIRO_OPERATOR_SOURCE ||
+	transparency == CAIRO_IMAGE_HAS_ALPHA ||
+	(transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA &&
+	 surface->ps_level == CAIRO_PS_LEVEL_2)) {
+	_cairo_ps_surface_flatten_image_transparency (surface,
+						      image,
+						      &opaque_image);
+	use_mask = FALSE;
+    } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) {
 	opaque_image = image;
+	use_mask = FALSE;
+    } else {
+	use_mask = TRUE;
     }
 
-    rgb_size = 3 * opaque_image->width * opaque_image->height;
+    rgb_size = 3 * image->width * image->height;
     rgb = malloc (rgb_size);
     if (rgb == NULL) {
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 	goto bail1;
     }
 
-    i = 0;
-    for (y = 0; y < opaque_image->height; y++) {
-	uint32_t *pixel = (uint32_t *) (opaque_image->data + y * opaque_image->stride);
-	for (x = 0; x < opaque_image->width; x++, pixel++) {
-	    rgb[i++] = (*pixel & 0x00ff0000) >> 16;
-	    rgb[i++] = (*pixel & 0x0000ff00) >>  8;
-	    rgb[i++] = (*pixel & 0x000000ff) >>  0;
+    if (use_mask) {
+	mask_size = (image->width * image->height + 7)/8;
+	mask = malloc (mask_size);
+	if (mask == NULL) {
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    goto bail2;
+	}
+    }
+
+    if (use_mask) {
+	int byte = 0;
+	int bit = 7;
+	i = 0;
+	for (y = 0; y < image->height; y++) {
+	    uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
+	    for (x = 0; x < image->width; x++, pixel++) {
+		if (bit == 7)
+		    mask[byte] = 0;
+		if (((*pixel & 0xff000000) >> 24) > 0x80)
+		    mask[byte] |= (1 << bit);
+		bit--;
+		if (bit < 0) {
+		    bit = 7;
+		    byte++;
+		}
+		rgb[i++] = (*pixel & 0x00ff0000) >> 16;
+		rgb[i++] = (*pixel & 0x0000ff00) >>  8;
+		rgb[i++] = (*pixel & 0x000000ff) >>  0;
+	    }
+	}
+    } else {
+	i = 0;
+	for (y = 0; y < opaque_image->height; y++) {
+	    uint32_t *pixel = (uint32_t *) (opaque_image->data + y * opaque_image->stride);
+	    for (x = 0; x < opaque_image->width; x++, pixel++) {
+		rgb[i++] = (*pixel & 0x00ff0000) >> 16;
+		rgb[i++] = (*pixel & 0x0000ff00) >>  8;
+		rgb[i++] = (*pixel & 0x000000ff) >>  0;
+	    }
 	}
     }
 
     /* XXX: Should fix cairo-lzw to provide a stream-based interface
      * instead. */
-    compressed_size = rgb_size;
-    compressed = _cairo_lzw_compress (rgb, &compressed_size);
-    if (compressed == NULL) {
+    rgb_compressed_size = rgb_size;
+    rgb_compressed = _cairo_lzw_compress (rgb, &rgb_compressed_size);
+    if (rgb_compressed == NULL) {
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	goto bail2;
+	goto bail3;
     }
 
     /* First emit the image data as a base85-encoded string which will
@@ -1853,65 +2041,128 @@ _cairo_ps_surface_emit_image (cairo_ps_s
     _cairo_output_stream_printf (surface->stream,
 				 "/%sData [\n", name);
 
-    string_array_stream = _string_array_stream_create (surface->stream);
-    status = _cairo_output_stream_get_status (string_array_stream);
-    if (status)
-	goto bail3;
-
-    base85_stream = _cairo_base85_stream_create (string_array_stream);
-    status = _cairo_output_stream_get_status (base85_stream);
-    if (status) {
-	status2 = _cairo_output_stream_destroy (string_array_stream);
-	goto bail3;
-    }
-
-    _cairo_output_stream_write (base85_stream, compressed, compressed_size);
-
-    status = _cairo_output_stream_destroy (base85_stream);
-    status2 = _cairo_output_stream_destroy (string_array_stream);
-    if (status == CAIRO_STATUS_SUCCESS)
-	status = status2;
+    status = _cairo_ps_surface_emit_base85_string (surface,
+						   rgb_compressed,
+						   rgb_compressed_size);
     if (status)
-	goto bail3;
+	goto bail4;
 
     _cairo_output_stream_printf (surface->stream,
 				 "] def\n");
     _cairo_output_stream_printf (surface->stream,
 				 "/%sDataIndex 0 def\n", name);
 
-    _cairo_output_stream_printf (surface->stream,
-				 "/%s {\n"
-				 "    /DeviceRGB setcolorspace\n"
-				 "    <<\n"
-				 "	/ImageType 1\n"
-				 "	/Width %d\n"
-				 "	/Height %d\n"
-				 "	/BitsPerComponent 8\n"
-				 "	/Decode [ 0 1 0 1 0 1 ]\n"
-				 "	/DataSource {\n"
-				 "	    %sData %sDataIndex get\n"
-				 "	    /%sDataIndex %sDataIndex 1 add def\n"
-				 "	    %sDataIndex %sData length 1 sub gt { /%sDataIndex 0 def } if\n"
-				 "	} /ASCII85Decode filter /LZWDecode filter\n"
-				 "	/ImageMatrix [ 1 0 0 1 0 0 ]\n"
-				 "    >>\n"
-				 "    image\n"
-				 "} def\n",
-				 name,
-				 opaque_image->width,
-				 opaque_image->height,
-				 name, name, name, name, name, name, name);
+    /* Emit the mask data as a base85-encoded string which will
+     * be used as the mask source for the image operator later. */
+    if (mask) {
+	mask_compressed_size = mask_size;
+	mask_compressed = _cairo_lzw_compress (mask, &mask_compressed_size);
+	if (mask_compressed == NULL) {
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    goto bail4;
+	}
+
+	_cairo_output_stream_printf (surface->stream,
+				     "/%sMask [\n", name);
+
+	status = _cairo_ps_surface_emit_base85_string (surface,
+						       mask_compressed,
+						       mask_compressed_size);
+	if (status)
+	    goto bail5;
+
+	_cairo_output_stream_printf (surface->stream,
+				     "] def\n");
+	_cairo_output_stream_printf (surface->stream,
+				     "/%sMaskIndex 0 def\n", name);
+    }
+
+    if (mask) {
+	_cairo_output_stream_printf (surface->stream,
+				     "/%s {\n"
+				     "    /DeviceRGB setcolorspace\n"
+				     "    <<\n"
+				     "	/ImageType 3\n"
+				     "	/InterleaveType 3\n"
+				     "	/DataDict <<\n"
+				     "		/ImageType 1\n"
+				     "		/Width %d\n"
+				     "		/Height %d\n"
+				     "		/BitsPerComponent 8\n"
+				     "		/Decode [ 0 1 0 1 0 1 ]\n"
+				     "		/DataSource {\n"
+				     "	    		%sData %sDataIndex get\n"
+				     "	    		/%sDataIndex %sDataIndex 1 add def\n"
+				     "	    		%sDataIndex %sData length 1 sub gt { /%sDataIndex 0 def } if\n"
+				     "		} /ASCII85Decode filter /LZWDecode filter\n"
+				     "		/ImageMatrix [ 1 0 0 1 0 0 ]\n"
+				     "	>>\n"
+				     "	/MaskDict <<\n"
+				     "		/ImageType 1\n"
+				     "		/Width %d\n"
+				     "		/Height %d\n"
+				     "		/BitsPerComponent 1\n"
+				     "		/Decode [ 1 0 ]\n"
+				     "		/DataSource {\n"
+				     "	    		%sMask %sMaskIndex get\n"
+				     "	    		/%sMaskIndex %sMaskIndex 1 add def\n"
+				     "	    		%sMaskIndex %sMask length 1 sub gt { /%sMaskIndex 0 def } if\n"
+				     "		} /ASCII85Decode filter /LZWDecode filter\n"
+				     "		/ImageMatrix [ 1 0 0 1 0 0 ]\n"
+				     "	>>\n"
+				     "    >>\n"
+				     "    image\n"
+				     "} def\n",
+				     name,
+				     image->width,
+				     image->height,
+				     name, name, name, name, name, name, name,
+				     image->width,
+				     image->height,
+				     name, name, name, name, name, name, name);
+    } else {
+	_cairo_output_stream_printf (surface->stream,
+				     "/%s {\n"
+				     "    /DeviceRGB setcolorspace\n"
+				     "    <<\n"
+				     "	/ImageType 1\n"
+				     "	/Width %d\n"
+				     "	/Height %d\n"
+				     "	/BitsPerComponent 8\n"
+				     "	/Decode [ 0 1 0 1 0 1 ]\n"
+				     "	/DataSource {\n"
+				     "	    %sData %sDataIndex get\n"
+				     "	    /%sDataIndex %sDataIndex 1 add def\n"
+				     "	    %sDataIndex %sData length 1 sub gt { /%sDataIndex 0 def } if\n"
+				     "	} /ASCII85Decode filter /LZWDecode filter\n"
+				     "	/ImageMatrix [ 1 0 0 1 0 0 ]\n"
+				     "    >>\n"
+				     "    image\n"
+				     "} def\n",
+				     name,
+				     opaque_image->width,
+				     opaque_image->height,
+				     name, name, name, name, name, name, name);
+    }
 
     status = CAIRO_STATUS_SUCCESS;
 
- bail3:
-    free (compressed);
- bail2:
+bail5:
+    if (use_mask)
+	free (mask_compressed);
+bail4:
+    free (rgb_compressed);
+
+bail3:
+    if (use_mask)
+	free (mask);
+bail2:
     free (rgb);
- bail1:
-    if (opaque_image != image)
-	cairo_surface_destroy (opaque);
- bail0:
+
+bail1:
+    if (!use_mask && opaque_image != image)
+	cairo_surface_destroy (&opaque_image->base);
+
     return status;
 }
 
@@ -1919,7 +2170,8 @@ static cairo_status_t
 _cairo_ps_surface_emit_image_surface (cairo_ps_surface_t      *surface,
 				      cairo_surface_pattern_t *pattern,
 				      int                     *width,
-				      int                     *height)
+				      int                     *height,
+				      cairo_operator_t 	       op)
 {
     cairo_image_surface_t  *image;
     void		   *image_extra;
@@ -1931,7 +2183,7 @@ _cairo_ps_surface_emit_image_surface (ca
     if (status)
 	return status;
 
-    _cairo_ps_surface_emit_image (surface, image, "CairoPattern");
+    _cairo_ps_surface_emit_image (surface, image, "CairoPattern", op);
     if (status)
 	goto fail;
 
@@ -2034,7 +2286,8 @@ _cairo_ps_surface_emit_solid_pattern (ca
 
 static cairo_status_t
 _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t      *surface,
-					cairo_surface_pattern_t *pattern)
+					cairo_surface_pattern_t *pattern,
+					cairo_operator_t 	 op)
 {
     cairo_status_t status;
     int pattern_width = 0; /* squelch bogus compiler warning */
@@ -2061,7 +2314,8 @@ _cairo_ps_surface_emit_surface_pattern (
 	status = _cairo_ps_surface_emit_image_surface (surface,
 						       pattern,
 						       &pattern_width,
-						       &pattern_height);
+						       &pattern_height,
+						       op);
 	if (status)
 	    return status;
     }
@@ -2359,7 +2613,9 @@ _cairo_ps_surface_emit_radial_pattern (c
 }
 
 static cairo_status_t
-_cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, cairo_pattern_t *pattern)
+_cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface,
+				cairo_pattern_t *pattern,
+				cairo_operator_t op)
 {
     /* FIXME: We should keep track of what pattern is currently set in
      * the postscript file and only emit code if we're setting a
@@ -2373,7 +2629,8 @@ _cairo_ps_surface_emit_pattern (cairo_ps
 
     case CAIRO_PATTERN_TYPE_SURFACE:
 	status = _cairo_ps_surface_emit_surface_pattern (surface,
-							 (cairo_surface_pattern_t *) pattern);
+							 (cairo_surface_pattern_t *) pattern,
+							 op);
 	if (status)
 	    return status;
 	break;
@@ -2497,7 +2754,7 @@ _cairo_ps_surface_paint (void			*abstrac
 
     _cairo_rectangle_intersect (&extents, &pattern_extents);
 
-    status = _cairo_ps_surface_emit_pattern (surface, source);
+    status = _cairo_ps_surface_emit_pattern (surface, source, op);
     if (status)
 	return status;
 
@@ -2640,7 +2897,7 @@ _cairo_ps_surface_stroke (void			*abstra
 	}
     }
 
-    status = _cairo_ps_surface_emit_pattern (surface, source);
+    status = _cairo_ps_surface_emit_pattern (surface, source, op);
     if (status)
 	return status;
 
@@ -2711,7 +2968,7 @@ _cairo_ps_surface_fill (void		*abstract_
 				 "%% _cairo_ps_surface_fill\n");
 #endif
 
-    status = _cairo_ps_surface_emit_pattern (surface, source);
+    status = _cairo_ps_surface_emit_pattern (surface, source, op);
     if (status)
 	return status;
 
@@ -2780,7 +3037,7 @@ _cairo_ps_surface_show_glyphs (void		   
 
     num_glyphs_unsigned = num_glyphs;
 
-    status = _cairo_ps_surface_emit_pattern (surface, source);
+    status = _cairo_ps_surface_emit_pattern (surface, source, op);
     if (status)
 	return status;
 
diff-tree af5cdde34d003b7d5943b169c160b91b8f7d10f3 (from ff35ffd2a7baa775e2ef73b844e783434c149057)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Oct 13 22:15:48 2007 +0930

    PS/Win32-printing: remove redundant checks from _analyze_operation
    
    The check for supported operators is performed earlier in this
    functions so these two checks in not required.

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 5171614..0e4e5bc 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1610,12 +1610,6 @@ _cairo_ps_surface_analyze_operation (cai
      * background to convert the pattern to opaque.
      */
 
-    if (_cairo_operator_always_opaque (op))
-	return CAIRO_STATUS_SUCCESS;
-
-    if (_cairo_operator_always_translucent (op))
-	return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
-
     if (_cairo_pattern_is_opaque (pattern))
 	return CAIRO_STATUS_SUCCESS;
     else
diff --git a/src/cairo-win32-printing-surface.c b/src/cairo-win32-printing-surface.c
index 26b0cfd..991249b 100644
--- a/src/cairo-win32-printing-surface.c
+++ b/src/cairo-win32-printing-surface.c
@@ -163,12 +163,6 @@ _cairo_win32_printing_surface_analyze_op
      * background to convert the pattern to opaque.
      */
 
-    if (_cairo_operator_always_opaque (op))
-	return CAIRO_STATUS_SUCCESS;
-
-    if (_cairo_operator_always_translucent (op))
-	return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
-
     if (_cairo_pattern_is_opaque (pattern))
 	return CAIRO_STATUS_SUCCESS;
     else
diff-tree ff35ffd2a7baa775e2ef73b844e783434c149057 (from 0b3f530973049737f942219452d991d76b65b724)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Oct 13 22:09:04 2007 +0930

    Add meta-surface-pattern test

diff --git a/test/.gitignore b/test/.gitignore
index 23026b6..d13f1a4 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -79,6 +79,7 @@ mask
 mask-alpha
 mask-ctm
 mask-surface-ctm
+meta-surface-pattern
 move-to-show-surface
 multi-page
 multi-page.pdf
diff --git a/test/Makefile.am b/test/Makefile.am
index 13381c3..3785568 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -62,6 +62,7 @@ mask				\
 mask-alpha			\
 mask-ctm			\
 mask-surface-ctm		\
+meta-surface-pattern            \
 move-to-show-surface		\
 new-sub-path			\
 nil-surface			\
diff --git a/test/meta-surface-pattern-ref.png b/test/meta-surface-pattern-ref.png
new file mode 100644
index 0000000..7f9c56c
Binary files /dev/null and b/test/meta-surface-pattern-ref.png differ
diff --git a/test/meta-surface-pattern-rgb24-ref.png b/test/meta-surface-pattern-rgb24-ref.png
new file mode 100644
index 0000000..2de298d
Binary files /dev/null and b/test/meta-surface-pattern-rgb24-ref.png differ
diff --git a/test/meta-surface-pattern.c b/test/meta-surface-pattern.c
new file mode 100644
index 0000000..4d280c7
--- /dev/null
+++ b/test/meta-surface-pattern.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright © 2007 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"
+
+static cairo_test_draw_function_t draw;
+
+#define PAT_WIDTH  120
+#define PAT_HEIGHT 120
+#define SIZE (PAT_WIDTH*2)
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+
+/* This test is designed to test painting a meta surface pattern with
+ * CAIRO_EXTEND_NONE and a non identity pattern matrix.
+ */
+
+cairo_test_t test = {
+    "meta-surface-pattern",
+    "Paint meta surface pattern with non identity pattern matrix",
+    WIDTH, HEIGHT,
+    draw
+};
+
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *pat_surface;
+    cairo_pattern_t *pattern;
+    cairo_matrix_t   mat;
+    cairo_t *cr2;
+
+    cairo_translate (cr, PAD, PAD);
+
+    pat_surface = cairo_surface_create_similar (cairo_get_target (cr),
+						CAIRO_CONTENT_COLOR_ALPHA,
+						PAT_WIDTH, PAT_HEIGHT);
+
+    cr2 = cairo_create (pat_surface);
+
+    cairo_set_source_rgba (cr2, 1, 0, 1, 0.5);
+    cairo_rectangle (cr2, PAT_WIDTH/6.0, PAT_HEIGHT/6.0, PAT_WIDTH/4.0, PAT_HEIGHT/4.0);
+    cairo_fill (cr2);
+
+    cairo_set_source_rgba (cr2, 0, 1, 1, 0.5);
+    cairo_rectangle (cr2, PAT_WIDTH/2.0, PAT_HEIGHT/2.0, PAT_WIDTH/4.0, PAT_HEIGHT/4.0);
+    cairo_fill (cr2);
+
+    cairo_set_line_width (cr2, 1);
+    cairo_move_to (cr2, PAT_WIDTH/6.0, 0);
+    cairo_line_to (cr2, 0, 0);
+    cairo_line_to (cr2, 0, PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr2, 1, 0, 0);
+    cairo_stroke (cr2);
+    cairo_move_to (cr2, PAT_WIDTH/6.0, PAT_HEIGHT);
+    cairo_line_to (cr2, 0, PAT_HEIGHT);
+    cairo_line_to (cr2, 0, 5*PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr2, 0, 1, 0);
+    cairo_stroke (cr2);
+    cairo_move_to (cr2, 5*PAT_WIDTH/6.0, 0);
+    cairo_line_to (cr2, PAT_WIDTH, 0);
+    cairo_line_to (cr2, PAT_WIDTH, PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr2, 0, 0, 1);
+    cairo_stroke (cr2);
+    cairo_move_to (cr2, 5*PAT_WIDTH/6.0, PAT_HEIGHT);
+    cairo_line_to (cr2, PAT_WIDTH, PAT_HEIGHT);
+    cairo_line_to (cr2, PAT_WIDTH, 5*PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr2, 1, 1, 0);
+    cairo_stroke (cr2);
+
+    cairo_set_source_rgb (cr2, 0.5, 0.5, 0.5);
+    cairo_set_line_width (cr2, PAT_WIDTH/10.0);
+
+    cairo_move_to (cr2, 0,         PAT_HEIGHT/4.0);
+    cairo_line_to (cr2, PAT_WIDTH, PAT_HEIGHT/4.0);
+    cairo_stroke (cr2);
+
+    cairo_move_to (cr2, PAT_WIDTH/4.0,         0);
+    cairo_line_to (cr2, PAT_WIDTH/4.0, PAT_WIDTH);
+    cairo_stroke (cr2);
+
+    cairo_destroy (cr2);
+    pattern = cairo_pattern_create_for_surface (pat_surface);
+    cairo_surface_destroy (pat_surface);
+
+    cairo_matrix_init_identity (&mat);
+    cairo_matrix_scale (&mat, 2, 1.5);
+    cairo_matrix_rotate (&mat, 1);
+    cairo_matrix_translate (&mat, -PAT_WIDTH/4.0, -PAT_WIDTH/2.0);
+    cairo_pattern_set_matrix (pattern, &mat);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_NONE);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+    return cairo_test (&test);
+}
diff-tree 0b3f530973049737f942219452d991d76b65b724 (from e66ce8cbc6e53b4b07a9af6bb7d2294ada0782c0)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Oct 13 21:49:50 2007 +0930

    Add PS reference images for tests using gradients

diff --git a/test/clip-operator-ps-argb32-ref.png b/test/clip-operator-ps-argb32-ref.png
new file mode 100644
index 0000000..1d49920
Binary files /dev/null and b/test/clip-operator-ps-argb32-ref.png differ
diff --git a/test/gradient-alpha-ps-argb32-ref.png b/test/gradient-alpha-ps-argb32-ref.png
new file mode 100644
index 0000000..8b93c05
Binary files /dev/null and b/test/gradient-alpha-ps-argb32-ref.png differ
diff --git a/test/gradient-alpha-ps-rgb24-ref.png b/test/gradient-alpha-ps-rgb24-ref.png
new file mode 100644
index 0000000..c3c96cb
Binary files /dev/null and b/test/gradient-alpha-ps-rgb24-ref.png differ
diff --git a/test/linear-gradient-ps-argb32-ref.png b/test/linear-gradient-ps-argb32-ref.png
new file mode 100644
index 0000000..e9202d8
Binary files /dev/null and b/test/linear-gradient-ps-argb32-ref.png differ
diff --git a/test/linear-gradient-ps-rgb24-ref.png b/test/linear-gradient-ps-rgb24-ref.png
new file mode 100644
index 0000000..e9202d8
Binary files /dev/null and b/test/linear-gradient-ps-rgb24-ref.png differ
diff --git a/test/operator-source-ps-argb32-ref.png b/test/operator-source-ps-argb32-ref.png
new file mode 100644
index 0000000..dda5e66
Binary files /dev/null and b/test/operator-source-ps-argb32-ref.png differ
diff --git a/test/operator-source-ps-rgb24-ref.png b/test/operator-source-ps-rgb24-ref.png
new file mode 100644
index 0000000..3137b15
Binary files /dev/null and b/test/operator-source-ps-rgb24-ref.png differ
diff --git a/test/text-pattern-ps-argb32-ref.png b/test/text-pattern-ps-argb32-ref.png
new file mode 100644
index 0000000..fa887f9
Binary files /dev/null and b/test/text-pattern-ps-argb32-ref.png differ
diff --git a/test/text-pattern-ps-rgb24-ref.png b/test/text-pattern-ps-rgb24-ref.png
new file mode 100644
index 0000000..2412bcb
Binary files /dev/null and b/test/text-pattern-ps-rgb24-ref.png differ
diff --git a/test/trap-clip-ps-argb32-ref.png b/test/trap-clip-ps-argb32-ref.png
index 7ed4951..aecd0f8 100644
Binary files a/test/trap-clip-ps-argb32-ref.png and b/test/trap-clip-ps-argb32-ref.png differ
diff --git a/test/trap-clip-ps-rgb24-ref.png b/test/trap-clip-ps-rgb24-ref.png
new file mode 100644
index 0000000..5bff374
Binary files /dev/null and b/test/trap-clip-ps-rgb24-ref.png differ
diff-tree e66ce8cbc6e53b4b07a9af6bb7d2294ada0782c0 (from 583059e4a3e86c937de13c07a50486f7ca77b335)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Oct 13 21:28:19 2007 +0930

    PS: Add linear and radial gradient support

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 86e0269..5171614 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1473,16 +1473,16 @@ _cairo_ps_surface_show_page (void *abstr
 }
 
 static cairo_bool_t
-color_is_gray (cairo_color_t *color)
+color_is_gray (double red, double green, double blue)
 {
     const double epsilon = 0.00001;
 
-    return (fabs (color->red - color->green) < epsilon &&
-	    fabs (color->red - color->blue) < epsilon);
+    return (fabs (red - green) < epsilon &&
+	    fabs (red - blue) < epsilon);
 }
 
 static cairo_bool_t
-surface_pattern_supported (const cairo_surface_pattern_t *pattern)
+surface_pattern_supported (cairo_surface_pattern_t *pattern)
 {
     cairo_extend_t extend;
 
@@ -1522,13 +1522,55 @@ surface_pattern_supported (const cairo_s
 }
 
 static cairo_bool_t
-pattern_supported (const cairo_pattern_t *pattern)
+_gradient_pattern_supported (cairo_ps_surface_t    *surface,
+			     cairo_pattern_t *pattern)
+{
+    cairo_extend_t extend;
+
+    if (surface->ps_level == CAIRO_PS_LEVEL_2)
+	return FALSE;
+
+    extend = cairo_pattern_get_extend (pattern);
+
+    if (extend == CAIRO_EXTEND_REPEAT ||
+        extend == CAIRO_EXTEND_REFLECT) {
+        return FALSE;
+    }
+
+    /* Radial gradients are currently only supported when one circle
+     * is inside the other. */
+    if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
+        double x1, y1, x2, y2, r1, r2, d;
+        cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
+
+        x1 = _cairo_fixed_to_double (radial->c1.x);
+        y1 = _cairo_fixed_to_double (radial->c1.y);
+        r1 = _cairo_fixed_to_double (radial->r1);
+        x2 = _cairo_fixed_to_double (radial->c2.x);
+        y2 = _cairo_fixed_to_double (radial->c2.y);
+        r2 = _cairo_fixed_to_double (radial->r2);
+
+        d = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
+        if (d > fabs(r2 - r1)) {
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static cairo_bool_t
+pattern_supported (cairo_ps_surface_t *surface, cairo_pattern_t *pattern)
 {
     if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
 	return TRUE;
 
+    if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
+	pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+	return _gradient_pattern_supported (surface, pattern);
+
     if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
-	return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern);
+	return surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
 
     return FALSE;
 }
@@ -1536,12 +1578,12 @@ pattern_supported (const cairo_pattern_t
 static cairo_int_status_t
 _cairo_ps_surface_analyze_operation (cairo_ps_surface_t    *surface,
 				     cairo_operator_t       op,
-				     const cairo_pattern_t *pattern)
+				     cairo_pattern_t *pattern)
 {
     if (surface->force_fallbacks && surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
-    if (! pattern_supported (pattern))
+    if (! pattern_supported (surface, pattern))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
     if (!(op == CAIRO_OPERATOR_SOURCE ||
@@ -1583,7 +1625,7 @@ _cairo_ps_surface_analyze_operation (cai
 static cairo_bool_t
 _cairo_ps_surface_operation_supported (cairo_ps_surface_t    *surface,
 				       cairo_operator_t       op,
-				       const cairo_pattern_t *pattern)
+				       cairo_pattern_t       *pattern)
 {
     if (_cairo_ps_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED)
 	return TRUE;
@@ -1953,31 +1995,40 @@ _cairo_ps_surface_emit_meta_surface (cai
 }
 
 static void
-_cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t    *surface,
-				      cairo_solid_pattern_t *pattern)
-{
-    cairo_color_t *color = &pattern->color;
-    double red, green, blue;
-
-    red = color->red;
-    green = color->green;
-    blue = color->blue;
+_cairo_ps_surface_flatten_transparency (cairo_ps_surface_t 	*surface,
+					const cairo_color_t 	*color,
+					double 			*red,
+					double 			*green,
+					double 			*blue)
+{
+    *red = color->red;
+    *green = color->green;
+    *blue = color->blue;
 
     if (!CAIRO_COLOR_IS_OPAQUE(color)) {
 	if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
 	    uint8_t one_minus_alpha = 255 - (color->alpha_short >> 8);
 
-	    red   = ((color->red_short   >> 8) + one_minus_alpha) / 255.0;
-	    green = ((color->green_short >> 8) + one_minus_alpha) / 255.0;
-	    blue  = ((color->blue_short  >> 8) + one_minus_alpha) / 255.0;
+	    *red   = ((color->red_short   >> 8) + one_minus_alpha) / 255.0;
+	    *green = ((color->green_short >> 8) + one_minus_alpha) / 255.0;
+	    *blue  = ((color->blue_short  >> 8) + one_minus_alpha) / 255.0;
 	} else {
-	    red   = (color->red_short   >> 8) / 255.0;
-	    green = (color->green_short >> 8) / 255.0;
-	    blue  = (color->blue_short  >> 8) / 255.0;
+	    *red   = (color->red_short   >> 8) / 255.0;
+	    *green = (color->green_short >> 8) / 255.0;
+	    *blue  = (color->blue_short  >> 8) / 255.0;
 	}
     }
+}
+
+static void
+_cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t    *surface,
+				      cairo_solid_pattern_t *pattern)
+{
+    double red, green, blue;
+
+    _cairo_ps_surface_flatten_transparency (surface, &pattern->color, &red, &green, &blue);
 
-    if (color_is_gray (color))
+    if (color_is_gray (red, green, blue))
 	_cairo_output_stream_printf (surface->stream,
 				     "%f G\n",
 				     red);
@@ -2089,18 +2140,228 @@ _cairo_ps_surface_emit_surface_pattern (
     return CAIRO_STATUS_SUCCESS;
 }
 
+typedef struct _cairo_ps_color_stop {
+    double offset;
+    double color[3];
+} cairo_ps_color_stop_t;
+
 static void
-_cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t *surface,
-		     cairo_linear_pattern_t *pattern)
+_cairo_ps_surface_emit_linear_colorgradient (cairo_ps_surface_t     *surface,
+					     cairo_ps_color_stop_t  *stop1,
+					     cairo_ps_color_stop_t  *stop2)
 {
-    /* XXX: NYI */
+    _cairo_output_stream_printf (surface->stream,
+				 "<< /FunctionType 2\n"
+				 "   /Domain [ 0 1 ]\n"
+				 "   /C0 [ %f %f %f ]\n"
+				 "   /C1 [ %f %f %f ]\n"
+				 "   /N 1\n"
+				 ">>\n",
+				 stop1->color[0],
+				 stop1->color[1],
+				 stop1->color[2],
+				 stop2->color[0],
+				 stop2->color[1],
+				 stop2->color[2]);
 }
 
 static void
-_cairo_ps_surface_emit_radial_pattern (cairo_ps_surface_t *surface,
-		     cairo_radial_pattern_t *pattern)
+_cairo_ps_surface_emit_stitched_colorgradient (cairo_ps_surface_t    *surface,
+					       unsigned int 	      n_stops,
+					       cairo_ps_color_stop_t  stops[])
 {
-    /* XXX: NYI */
+    unsigned int i;
+
+    _cairo_output_stream_printf (surface->stream,
+				 "      << /FunctionType 3\n"
+				 "         /Domain [ 0 1 ]\n"
+				 "         /Functions [\n");
+    for (i = 0; i < n_stops - 1; i++)
+	_cairo_ps_surface_emit_linear_colorgradient (surface, &stops[i], &stops[i+1]);
+
+    _cairo_output_stream_printf (surface->stream, "         ]\n");
+
+    _cairo_output_stream_printf (surface->stream, "         /Bounds [ ");
+    for (i = 1; i < n_stops-1; i++)
+	_cairo_output_stream_printf (surface->stream, "%f ", stops[i].offset);
+    _cairo_output_stream_printf (surface->stream, "]\n");
+
+    _cairo_output_stream_printf (surface->stream, "         /Encode [ ");
+    for (i = 1; i < n_stops; i++)
+	_cairo_output_stream_printf (surface->stream, "0 1 ");
+    _cairo_output_stream_printf (surface->stream,  "]\n");
+
+    _cairo_output_stream_printf (surface->stream, "      >>\n");
+}
+
+#define COLOR_STOP_EPSILON 1e-6
+
+static cairo_status_t
+_cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t       *surface,
+				      cairo_gradient_pattern_t *pattern)
+{
+    cairo_ps_color_stop_t *allstops, *stops;
+    unsigned int i, n_stops;
+
+    allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_ps_color_stop_t));
+    if (allstops == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    stops = &allstops[1];
+    n_stops = pattern->n_stops;
+
+    for (i = 0; i < n_stops; i++) {
+	double red, green, blue;
+
+	_cairo_ps_surface_flatten_transparency (surface,
+						&pattern->stops[i].color,
+						&red, &green, &blue);
+	stops[i].color[0] = red;
+	stops[i].color[1] = green;
+	stops[i].color[2] = blue;
+	stops[i].offset = _cairo_fixed_to_double (pattern->stops[i].x);
+    }
+
+    /* make sure first offset is 0.0 and last offset is 1.0 */
+    if (stops[0].offset > COLOR_STOP_EPSILON) {
+	memcpy (allstops, stops, sizeof (cairo_ps_color_stop_t));
+	stops = allstops;
+	n_stops++;
+    }
+    stops[0].offset = 0.0;
+
+    if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) {
+	memcpy (&stops[n_stops],
+		&stops[n_stops - 1],
+		sizeof (cairo_ps_color_stop_t));
+	n_stops++;
+    }
+    stops[n_stops-1].offset = 1.0;
+
+    if (n_stops == 2) {
+	/* no need for stitched function */
+	_cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[1]);
+    } else {
+	/* multiple stops: stitch. XXX possible optimization: regulary spaced
+	 * stops do not require stitching. XXX */
+	_cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops,stops);
+    }
+
+    free (allstops);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t     *surface,
+				       cairo_linear_pattern_t *pattern)
+{
+    double x1, y1, x2, y2;
+    cairo_extend_t extend;
+    cairo_status_t status;
+    cairo_matrix_t inverse = pattern->base.base.matrix;
+
+    extend = cairo_pattern_get_extend (&pattern->base.base);
+
+    status = cairo_matrix_invert (&inverse);
+    if (status)
+	return status;
+
+    x1 = _cairo_fixed_to_double (pattern->p1.x);
+    y1 = _cairo_fixed_to_double (pattern->p1.y);
+    x2 = _cairo_fixed_to_double (pattern->p2.x);
+    y2 = _cairo_fixed_to_double (pattern->p2.y);
+
+    _cairo_output_stream_printf (surface->stream,
+				 "<< /PatternType 2\n"
+				 "   /Shading\n"
+				 "   << /ShadingType 2\n"
+				 "      /ColorSpace /DeviceRGB\n"
+				 "      /Coords [ %f %f %f %f ]\n"
+				 "      /Function\n",
+				 x1, y1, x2, y2);
+
+    status = _cairo_ps_surface_emit_pattern_stops (surface, &pattern->base);
+    if (status)
+	return status;
+
+    if (extend == CAIRO_EXTEND_PAD) {
+	_cairo_output_stream_printf (surface->stream,
+                                     "      /Extend [ true true ]\r\n");
+    } else {
+	_cairo_output_stream_printf (surface->stream,
+                                     "      /Extend [ false false ]\r\n");
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+				 "   >>\n"
+				 ">>\n");
+    _cairo_output_stream_printf (surface->stream,
+				 "[ %f %f %f %f %f %f ]\n",
+				 inverse.xx, inverse.yx,
+				 inverse.xy, inverse.yy,
+				 inverse.x0, inverse.y0);
+    _cairo_output_stream_printf (surface->stream,
+				 "makepattern setpattern\n");
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_radial_pattern (cairo_ps_surface_t     *surface,
+				       cairo_radial_pattern_t *pattern)
+{
+    double x1, y1, x2, y2, r1, r2;
+    cairo_extend_t extend;
+    cairo_status_t status;
+    cairo_matrix_t inverse = pattern->base.base.matrix;
+
+    extend = cairo_pattern_get_extend (&pattern->base.base);
+
+    status = cairo_matrix_invert (&inverse);
+    if (status)
+	return status;
+
+    x1 = _cairo_fixed_to_double (pattern->c1.x);
+    y1 = _cairo_fixed_to_double (pattern->c1.y);
+    r1 = _cairo_fixed_to_double (pattern->r1);
+    x2 = _cairo_fixed_to_double (pattern->c2.x);
+    y2 = _cairo_fixed_to_double (pattern->c2.y);
+    r2 = _cairo_fixed_to_double (pattern->r2);
+
+    _cairo_output_stream_printf (surface->stream,
+				 "<< /PatternType 2\n"
+				 "   /Shading\n"
+				 "   << /ShadingType 3\n"
+				 "      /ColorSpace /DeviceRGB\n"
+				 "      /Coords [ %f %f %f %f %f %f ]\n"
+				 "      /Function\n",
+				 x1, y1, r1, x2, y2, r2);
+
+    status = _cairo_ps_surface_emit_pattern_stops (surface, &pattern->base);
+    if (status)
+	return status;
+
+    if (extend == CAIRO_EXTEND_PAD) {
+	_cairo_output_stream_printf (surface->stream,
+                                     "      /Extend [ true true ]\r\n");
+    } else {
+	_cairo_output_stream_printf (surface->stream,
+                                     "      /Extend [ false false ]\r\n");
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+				 "   >>\n"
+				 ">>\n");
+    _cairo_output_stream_printf (surface->stream,
+				 "[ %f %f %f %f %f %f ]\n",
+				 inverse.xx, inverse.yx,
+				 inverse.xy, inverse.yy,
+				 inverse.x0, inverse.y0);
+    _cairo_output_stream_printf (surface->stream,
+				 "makepattern setpattern\n");
+
+    return status;
 }
 
 static cairo_status_t
diff-tree 583059e4a3e86c937de13c07a50486f7ca77b335 (from b5c36010edc92ca129a0498cf04592f8f692f800)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Oct 13 21:16:49 2007 +0930

    Fix bug in _gradient_is_opaque()

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 6eea9a6..488177b 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1469,7 +1469,7 @@ _gradient_is_opaque (const cairo_gradien
     unsigned int i;
 
     for (i = 0; i < gradient->n_stops; i++)
-	if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (gradient->stops[i].color.alpha))
+	if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color))
 	    return FALSE;
 
     return TRUE;
diff-tree b5c36010edc92ca129a0498cf04592f8f692f800 (from b5fa273c9a4f730882c6edade785236e72b2b4db)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Oct 13 21:14:28 2007 +0930

    Add cairo_ps_surface_restrict_to_level() API

diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt
index a06692e..5f450e9 100644
--- a/doc/public/cairo-sections.txt
+++ b/doc/public/cairo-sections.txt
@@ -58,6 +58,10 @@ cairo_surface_write_to_png_stream
 <TITLE>PostScript Surfaces</TITLE>
 cairo_ps_surface_create
 cairo_ps_surface_create_for_stream
+cairo_ps_surface_restrict_to_level
+cairo_ps_level_t
+cairo_ps_get_levels
+cairo_ps_level_to_string
 cairo_ps_surface_set_eps
 cairo_ps_surface_get_eps
 cairo_ps_surface_set_size
diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h
index d0f4b0b..d75f6c7 100644
--- a/src/cairo-ps-surface-private.h
+++ b/src/cairo-ps-surface-private.h
@@ -77,6 +77,8 @@ typedef struct cairo_ps_surface {
 
     cairo_array_t *dsc_comment_target;
 
+    cairo_ps_level_t ps_level;
+
     cairo_surface_t *paginated_surface;
 } cairo_ps_surface_t;
 
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index b2b0d03..86e0269 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -54,6 +54,20 @@
 static const cairo_surface_backend_t cairo_ps_surface_backend;
 static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend;
 
+static const cairo_ps_level_t _cairo_ps_levels[] =
+{
+    CAIRO_PS_LEVEL_2,
+    CAIRO_PS_LEVEL_3
+};
+
+#define CAIRO_PS_LEVEL_LAST ARRAY_LENGTH (_cairo_ps_levels)
+
+static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] =
+{
+    "PS Level 2",
+    "PS Level 3"
+};
+
 /* A word wrap stream can be used as a filter to do word wrapping on
  * top of an existing output stream. The word wrapping is quite
  * simple, using isspace to determine characters that separate
@@ -299,10 +313,16 @@ _cairo_ps_surface_emit_header (cairo_ps_
     time_t now;
     char **comments;
     int i, num_comments;
+    const char *level;
     const char *eps_header = "";
 
     now = time (NULL);
 
+    if (surface->ps_level == CAIRO_PS_LEVEL_2)
+	level = "2";
+    else
+	level = "3";
+
     if (surface->eps)
 	eps_header = " EPSF-3.0";
 
@@ -323,7 +343,8 @@ _cairo_ps_surface_emit_header (cairo_ps_
 
     _cairo_output_stream_printf (surface->final_stream,
 				 "%%%%DocumentData: Clean7Bit\n"
-				 "%%%%LanguageLevel: 2\n");
+				 "%%%%LanguageLevel: %s\n",
+				 level);
 
     num_comments = _cairo_array_num_elements (&surface->dsc_header_comments);
     comments = _cairo_array_index (&surface->dsc_header_comments, 0);
@@ -840,6 +861,7 @@ _cairo_ps_surface_create_for_stream_inte
 	goto CLEANUP_OUTPUT_STREAM;
 
     surface->eps = FALSE;
+    surface->ps_level = CAIRO_PS_LEVEL_3;
     surface->width  = width;
     surface->height = height;
     surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
@@ -995,6 +1017,81 @@ _extract_ps_surface (cairo_surface_t	 *s
 }
 
 /**
+ * cairo_ps_surface_restrict_to_level:
+ * @surface: a PostScript #cairo_surface_t
+ * @level: PostScript level
+ *
+ * Restricts the generated PostSript file to @level. See
+ * cairo_ps_get_levels() for a list of available level values that
+ * can be used here.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the given surface. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface.
+ *
+ * Since: 1.6
+ **/
+void
+cairo_ps_surface_restrict_to_level (cairo_surface_t  *surface,
+                                    cairo_ps_level_t  level)
+{
+    cairo_ps_surface_t *ps_surface;
+    cairo_status_t status;
+
+    status = _extract_ps_surface (surface, &ps_surface);
+    if (status) {
+	status = _cairo_surface_set_error (surface, status);
+	return;
+    }
+
+    if (level < CAIRO_PS_LEVEL_LAST)
+	ps_surface->ps_level = level;
+}
+
+/**
+ * cairo_ps_get_levels:
+ * @levels: supported level list
+ * @num_levels: list length
+ *
+ * Used to retrieve the list of supported levels. See
+ * cairo_ps_surface_restrict_to_level().
+ *
+ * Since: 1.6
+ **/
+void
+cairo_ps_get_levels (cairo_ps_level_t const	**levels,
+                     int                     	 *num_levels)
+{
+    if (levels != NULL)
+	*levels = _cairo_ps_levels;
+
+    if (num_levels != NULL)
+	*num_levels = CAIRO_PS_LEVEL_LAST;
+}
+
+/**
+ * cairo_ps_level_to_string:
+ * @level: a level id
+ *
+ * Get the string representation of the given @level id. This function
+ * will return NULL if @level id isn't valid. See cairo_ps_get_levels()
+ * for a way to get the list of valid level ids.
+ *
+ * Return value: the string associated to given level.
+ *
+ * Since: 1.6
+ **/
+const char *
+cairo_ps_level_to_string (cairo_ps_level_t level)
+{
+    if (level >= CAIRO_PS_LEVEL_LAST)
+	return NULL;
+
+    return _cairo_ps_level_strings[level];
+}
+
+/**
  * cairo_ps_surface_set_eps:
  * @surface: a PostScript cairo_surface_t
  * @eps: TRUE to output EPS format PostScript
diff --git a/src/cairo-ps.h b/src/cairo-ps.h
index 24dd9f6..f3ac290 100644
--- a/src/cairo-ps.h
+++ b/src/cairo-ps.h
@@ -47,6 +47,20 @@ CAIRO_BEGIN_DECLS
 
 /* PS-surface functions */
 
+/**
+ * cairo_ps_level_t
+ * @CAIRO_PS_LEVEL_2: The language level 2 of the PostScript specification.
+ * @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification.
+ *
+ * #cairo_ps_level_t is used to describe the language level of the
+ * PostScript Language Reference that a generated PostScript file will
+ * conform to.
+ */
+typedef enum _cairo_ps_level {
+    CAIRO_PS_LEVEL_2,
+    CAIRO_PS_LEVEL_3
+} cairo_ps_level_t;
+
 cairo_public cairo_surface_t *
 cairo_ps_surface_create (const char		*filename,
 			 double			 width_in_points,
@@ -59,6 +73,17 @@ cairo_ps_surface_create_for_stream (cair
 				    double		height_in_points);
 
 cairo_public void
+cairo_ps_surface_restrict_to_level (cairo_surface_t    *surface,
+                                    cairo_ps_level_t    level);
+
+cairo_public void
+cairo_ps_get_levels (cairo_ps_level_t const  **levels,
+                     int                      *num_levels);
+
+cairo_public const char *
+cairo_ps_level_to_string (cairo_ps_level_t level);
+
+cairo_public void
 cairo_ps_surface_set_eps (cairo_surface_t	*surface,
 			  cairo_bool_t           eps);
 
diff-tree b5fa273c9a4f730882c6edade785236e72b2b4db (from 8520ce31be13bc939357cd45bd1b4c1967d7ff53)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Oct 13 21:04:51 2007 +0930

    Add PS reference images for tests using create_similar
    
    before the PS meta surface pattern patch these tests
    had image fallacks in the PS output and did not require
    a PS reference image.

diff --git a/test/clip-fill-rule-ps-rgb24-ref.png b/test/clip-fill-rule-ps-rgb24-ref.png
new file mode 100644
index 0000000..60e43c4
Binary files /dev/null and b/test/clip-fill-rule-ps-rgb24-ref.png differ
diff --git a/test/clip-nesting-ps-rgb24-ref.png b/test/clip-nesting-ps-rgb24-ref.png
new file mode 100644
index 0000000..8238deb
Binary files /dev/null and b/test/clip-nesting-ps-rgb24-ref.png differ
diff --git a/test/clip-push-group-ps-argb32-ref.png b/test/clip-push-group-ps-argb32-ref.png
new file mode 100644
index 0000000..31b3284
Binary files /dev/null and b/test/clip-push-group-ps-argb32-ref.png differ
diff --git a/test/clip-push-group-ps-rgb24-ref.png b/test/clip-push-group-ps-rgb24-ref.png
new file mode 100644
index 0000000..31b3284
Binary files /dev/null and b/test/clip-push-group-ps-rgb24-ref.png differ
diff --git a/test/clip-twice-ps-rgb24-ref.png b/test/clip-twice-ps-rgb24-ref.png
new file mode 100644
index 0000000..8a121c4
Binary files /dev/null and b/test/clip-twice-ps-rgb24-ref.png differ
diff --git a/test/copy-path-ps-rgb24-ref.png b/test/copy-path-ps-rgb24-ref.png
new file mode 100644
index 0000000..aa27262
Binary files /dev/null and b/test/copy-path-ps-rgb24-ref.png differ
diff --git a/test/dash-caps-joins-ps-rgb24-ref.png b/test/dash-caps-joins-ps-rgb24-ref.png
new file mode 100644
index 0000000..c62f48d
Binary files /dev/null and b/test/dash-caps-joins-ps-rgb24-ref.png differ
diff --git a/test/dash-scale-ps-rgb24-ref.png b/test/dash-scale-ps-rgb24-ref.png
new file mode 100644
index 0000000..5a92f55
Binary files /dev/null and b/test/dash-scale-ps-rgb24-ref.png differ
diff --git a/test/dash-state-ps-rgb24-ref.png b/test/dash-state-ps-rgb24-ref.png
new file mode 100644
index 0000000..e655974
Binary files /dev/null and b/test/dash-state-ps-rgb24-ref.png differ
diff --git a/test/degenerate-path-ps-rgb24-ref.png b/test/degenerate-path-ps-rgb24-ref.png
new file mode 100644
index 0000000..99e6c66
Binary files /dev/null and b/test/degenerate-path-ps-rgb24-ref.png differ
diff --git a/test/fill-and-stroke-ps-rgb24-ref.png b/test/fill-and-stroke-ps-rgb24-ref.png
new file mode 100644
index 0000000..41e3991
Binary files /dev/null and b/test/fill-and-stroke-ps-rgb24-ref.png differ
diff --git a/test/fill-missed-stop-ps-rgb24-ref.png b/test/fill-missed-stop-ps-rgb24-ref.png
new file mode 100644
index 0000000..fd54c7b
Binary files /dev/null and b/test/fill-missed-stop-ps-rgb24-ref.png differ
diff --git a/test/fill-rule-ps-rgb24-ref.png b/test/fill-rule-ps-rgb24-ref.png
new file mode 100644
index 0000000..8e1efc2
Binary files /dev/null and b/test/fill-rule-ps-rgb24-ref.png differ
diff --git a/test/font-matrix-translation-ps-rgb24-ref.png b/test/font-matrix-translation-ps-rgb24-ref.png
new file mode 100644
index 0000000..330a1b5
Binary files /dev/null and b/test/font-matrix-translation-ps-rgb24-ref.png differ
diff --git a/test/leaky-dash-ps-rgb24-ref.png b/test/leaky-dash-ps-rgb24-ref.png
new file mode 100644
index 0000000..c11eb48
Binary files /dev/null and b/test/leaky-dash-ps-rgb24-ref.png differ
diff --git a/test/line-width-scale-ps-rgb24-ref.png b/test/line-width-scale-ps-rgb24-ref.png
new file mode 100644
index 0000000..12c5b52
Binary files /dev/null and b/test/line-width-scale-ps-rgb24-ref.png differ
diff --git a/test/new-sub-path-ps-rgb24-ref.png b/test/new-sub-path-ps-rgb24-ref.png
new file mode 100644
index 0000000..bceb5b7
Binary files /dev/null and b/test/new-sub-path-ps-rgb24-ref.png differ
diff --git a/test/pixman-rotate-ps-argb32-ref.png b/test/pixman-rotate-ps-argb32-ref.png
new file mode 100644
index 0000000..1d6ad04
Binary files /dev/null and b/test/pixman-rotate-ps-argb32-ref.png differ
diff --git a/test/pixman-rotate-ps-rgb24-ref.png b/test/pixman-rotate-ps-rgb24-ref.png
new file mode 100644
index 0000000..d8ecce9
Binary files /dev/null and b/test/pixman-rotate-ps-rgb24-ref.png differ
diff --git a/test/random-intersections-ps-rgb24-ref.png b/test/random-intersections-ps-rgb24-ref.png
new file mode 100644
index 0000000..2a199ff
Binary files /dev/null and b/test/random-intersections-ps-rgb24-ref.png differ
diff --git a/test/rel-path-ps-rgb24-ref.png b/test/rel-path-ps-rgb24-ref.png
new file mode 100644
index 0000000..ccdcebb
Binary files /dev/null and b/test/rel-path-ps-rgb24-ref.png differ
diff --git a/test/rotate-image-surface-paint-ps-rgb24-ref.png b/test/rotate-image-surface-paint-ps-rgb24-ref.png
new file mode 100644
index 0000000..4ddcfae
Binary files /dev/null and b/test/rotate-image-surface-paint-ps-rgb24-ref.png differ
diff --git a/test/source-clip-scale-ps-argb32-ref.png b/test/source-clip-scale-ps-argb32-ref.png
new file mode 100644
index 0000000..be57d77
Binary files /dev/null and b/test/source-clip-scale-ps-argb32-ref.png differ
diff --git a/test/source-clip-scale-ps-rgb24-ref.png b/test/source-clip-scale-ps-rgb24-ref.png
new file mode 100644
index 0000000..be57d77
Binary files /dev/null and b/test/source-clip-scale-ps-rgb24-ref.png differ
diff --git a/test/text-rotate-ps-rgb24-ref.png b/test/text-rotate-ps-rgb24-ref.png
new file mode 100644
index 0000000..3c6759e
Binary files /dev/null and b/test/text-rotate-ps-rgb24-ref.png differ
diff-tree 8520ce31be13bc939357cd45bd1b4c1967d7ff53 (from e5b01e67975578797b4f7875a9ba02121ff935b5)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Oct 13 20:17:43 2007 +0930

    PS: Add meta surface pattern support

diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
index 4d7c7e9..d8a08da 100644
--- a/boilerplate/cairo-boilerplate.c
+++ b/boilerplate/cairo-boilerplate.c
@@ -262,16 +262,7 @@ static cairo_boilerplate_target_t target
       _cairo_boilerplate_ps_surface_write_to_png,
       _cairo_boilerplate_ps_cleanup,
       NULL, TRUE },
-
-    /* XXX: We expect type image here only due to a limitation in
-     * the current PS/meta-surface code. A PS surface is
-     * "naturally" COLOR_ALPHA, so the COLOR-only variant goes
-     * through create_similar in _cairo_boilerplate_ps_create_surface which results
-     * in the similar surface being used as a source. We do not yet
-     * have source support for PS/meta-surfaces, so the
-     * create_similar path for all paginated surfaces currently
-     * returns an image surface.*/
-    { "ps", CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
+    { "ps", CAIRO_INTERNAL_SURFACE_TYPE_META, CAIRO_CONTENT_COLOR, 0,
       _cairo_boilerplate_ps_create_surface,
       _cairo_boilerplate_ps_surface_write_to_png,
       _cairo_boilerplate_ps_cleanup,
diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h
index eea7e17..d0f4b0b 100644
--- a/src/cairo-ps-surface-private.h
+++ b/src/cairo-ps-surface-private.h
@@ -58,7 +58,7 @@ typedef struct cairo_ps_surface {
     cairo_output_stream_t *stream;
 
     cairo_bool_t eps;
-
+    cairo_content_t content;
     double width;
     double height;
     int bbox_x1, bbox_y1, bbox_x2, bbox_y2;
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 77ef97e..b2b0d03 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -844,6 +844,7 @@ _cairo_ps_surface_create_for_stream_inte
     surface->height = height;
     surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
     surface->force_fallbacks = FALSE;
+    surface->content = CAIRO_CONTENT_COLOR_ALPHA;
 
     surface->num_pages = 0;
 
@@ -1288,6 +1289,15 @@ cairo_ps_surface_dsc_begin_page_setup (c
     }
 }
 
+static cairo_surface_t *
+_cairo_ps_surface_create_similar (void			*abstract_surface,
+				  cairo_content_t	 content,
+				  int			 width,
+				  int			 height)
+{
+    return _cairo_meta_surface_create (content, width, height);
+}
+
 static cairo_status_t
 _cairo_ps_surface_finish (void *abstract_surface)
 {
@@ -1379,6 +1389,9 @@ surface_pattern_supported (const cairo_s
 {
     cairo_extend_t extend;
 
+    if (_cairo_surface_is_meta (pattern->surface))
+	return TRUE;
+
     if (pattern->surface->backend->acquire_source_image == NULL)
 	return FALSE;
 
@@ -1434,12 +1447,20 @@ _cairo_ps_surface_analyze_operation (cai
     if (! pattern_supported (pattern))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
+    if (!(op == CAIRO_OPERATOR_SOURCE ||
+	  op == CAIRO_OPERATOR_OVER))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+	cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+	if ( _cairo_surface_is_meta (surface_pattern->surface)) {
+	    return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN;
+	}
+    }
+
     if (op == CAIRO_OPERATOR_SOURCE)
 	return CAIRO_STATUS_SUCCESS;
 
-    if (op != CAIRO_OPERATOR_OVER)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-
     /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If
      * the pattern contains transparency, we return
      * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis
@@ -1613,12 +1634,20 @@ _cairo_ps_surface_emit_image (cairo_ps_s
     cairo_output_stream_t *base85_stream, *string_array_stream;
 
     /* PostScript can not represent the alpha channel, so we blend the
-       current image over a white RGB surface to eliminate it. */
+       current image over a white (or black for CONTENT_COLOR
+       surfaces) RGB surface to eliminate it. */
 
     if (image->base.status)
 	return image->base.status;
 
     if (image->format != CAIRO_FORMAT_RGB24) {
+	const cairo_color_t *background_color;
+
+	if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
+	    background_color = CAIRO_COLOR_WHITE;
+	else
+	    background_color = CAIRO_COLOR_BLACK;
+
 	opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
 					     image->width,
 					     image->height);
@@ -1632,7 +1661,7 @@ _cairo_ps_surface_emit_image (cairo_ps_s
 
 	status = _cairo_surface_fill_rectangle (opaque,
 				                CAIRO_OPERATOR_SOURCE,
-						CAIRO_COLOR_WHITE,
+						background_color,
 						0, 0,
 					       	image->width, image->height);
 	if (status) {
@@ -1753,6 +1782,79 @@ _cairo_ps_surface_emit_image (cairo_ps_s
     return status;
 }
 
+static cairo_status_t
+_cairo_ps_surface_emit_image_surface (cairo_ps_surface_t      *surface,
+				      cairo_surface_pattern_t *pattern,
+				      int                     *width,
+				      int                     *height)
+{
+    cairo_image_surface_t  *image;
+    void		   *image_extra;
+    cairo_status_t          status;
+
+    status = _cairo_surface_acquire_source_image (pattern->surface,
+						  &image,
+						  &image_extra);
+    if (status)
+	return status;
+
+    _cairo_ps_surface_emit_image (surface, image, "CairoPattern");
+    if (status)
+	goto fail;
+
+    *width = image->width;
+    *height = image->height;
+
+fail:
+    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t  *surface,
+				     cairo_surface_t      *meta_surface)
+{
+    double old_width, old_height;
+    cairo_content_t old_content;
+    cairo_rectangle_int_t meta_extents;
+    cairo_status_t status;
+
+    status = _cairo_surface_get_extents (meta_surface, &meta_extents);
+    if (status)
+	return status;
+
+    old_content = surface->content;
+    old_width = surface->width;
+    old_height = surface->height;
+    surface->width = meta_extents.width;
+    surface->height = meta_extents.height;
+    _cairo_output_stream_printf (surface->stream,
+				 "/CairoPattern {\n"
+				 "gsave\n");
+
+    if (cairo_surface_get_content (meta_surface) == CAIRO_CONTENT_COLOR) {
+	surface->content = CAIRO_CONTENT_COLOR;
+	_cairo_output_stream_printf (surface->stream,
+				     "0 G 0 0 %f %f rectfill\n",
+				     surface->width,
+				     surface->height);
+    }
+
+    status = _cairo_meta_surface_replay (meta_surface, &surface->base);
+    if (status)
+	return status;
+
+    _cairo_output_stream_printf (surface->stream,
+				 "grestore\n"
+				 "} bind def\n");
+    surface->content = old_content;
+    surface->width = old_width;
+    surface->height = old_height;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static void
 _cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t    *surface,
 				      cairo_solid_pattern_t *pattern)
@@ -1765,11 +1867,17 @@ _cairo_ps_surface_emit_solid_pattern (ca
     blue = color->blue;
 
     if (!CAIRO_COLOR_IS_OPAQUE(color)) {
-	uint8_t one_minus_alpha = 255 - (color->alpha_short >> 8);
+	if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
+	    uint8_t one_minus_alpha = 255 - (color->alpha_short >> 8);
 
-	red   = ((color->red_short   >> 8) + one_minus_alpha) / 255.0;
-	green = ((color->green_short >> 8) + one_minus_alpha) / 255.0;
-	blue  = ((color->blue_short  >> 8) + one_minus_alpha) / 255.0;
+	    red   = ((color->red_short   >> 8) + one_minus_alpha) / 255.0;
+	    green = ((color->green_short >> 8) + one_minus_alpha) / 255.0;
+	    blue  = ((color->blue_short  >> 8) + one_minus_alpha) / 255.0;
+	} else {
+	    red   = (color->red_short   >> 8) / 255.0;
+	    green = (color->green_short >> 8) / 255.0;
+	    blue  = (color->blue_short  >> 8) / 255.0;
+	}
     }
 
     if (color_is_gray (color))
@@ -1787,7 +1895,8 @@ _cairo_ps_surface_emit_surface_pattern (
 					cairo_surface_pattern_t *pattern)
 {
     cairo_status_t status;
-    double bbox_width, bbox_height;
+    int pattern_width = 0; /* squelch bogus compiler warning */
+    int pattern_height = 0; /* squelch bogus compiler warning */
     double xstep, ystep;
     cairo_matrix_t inverse = pattern->base.matrix;
 
@@ -1796,97 +1905,81 @@ _cairo_ps_surface_emit_surface_pattern (
     assert (status == CAIRO_STATUS_SUCCESS);
 
     if (_cairo_surface_is_meta (pattern->surface)) {
-	_cairo_output_stream_printf (surface->stream, "/MyPattern {\n");
+	cairo_surface_t *meta_surface = pattern->surface;
+	cairo_rectangle_int_t pattern_extents;
 
-	status = _cairo_meta_surface_replay (pattern->surface, &surface->base);
+	status = _cairo_ps_surface_emit_meta_surface (surface,
+						      meta_surface);
+	status = _cairo_surface_get_extents (meta_surface, &pattern_extents);
 	if (status)
 	    return status;
-
-	bbox_width = surface->width;
-	bbox_height = surface->height;
-	xstep = surface->width;
-	ystep = surface->height;
-	_cairo_output_stream_printf (surface->stream, "} bind def\n");
+	pattern_width = pattern_extents.width;
+	pattern_height = pattern_extents.height;
     } else {
-	cairo_image_surface_t	*image;
-	void			*image_extra;
-	cairo_status_t		status;
-
-	status = _cairo_surface_acquire_source_image (pattern->surface,
-						      &image,
-						      &image_extra);
+	status = _cairo_ps_surface_emit_image_surface (surface,
+						       pattern,
+						       &pattern_width,
+						       &pattern_height);
 	if (status)
 	    return status;
+    }
 
-	status = _cairo_ps_surface_emit_image (surface, image, "MyPattern");
-	if (status) {
-	    _cairo_surface_release_source_image (pattern->surface,
-		                                 image, image_extra);
-	    return status;
-	}
-
-	bbox_width = image->width;
-	bbox_height = image->height;
-
-	switch (pattern->base.extend) {
+    switch (pattern->base.extend) {
 	/* We implement EXTEND_PAD like EXTEND_NONE for now */
-	case CAIRO_EXTEND_PAD:
-	case CAIRO_EXTEND_NONE:
-	{
-	    /* In PS/PDF, (as far as I can tell), all patterns are
-	     * repeating. So we support cairo's EXTEND_NONE semantics
-	     * by setting the repeat step size to a size large enough
-	     * to guarantee that no more than a single occurrence will
-	     * be visible.
-	     *
-	     * First, map the surface extents into pattern space (since
-	     * xstep and ystep are in pattern space).  Then use an upper
-	     * bound on the length of the diagonal of the pattern image
-	     * and the surface as repeat size.  This guarantees to never
-	     * repeat visibly.
-	     */
-	    double x1 = 0.0, y1 = 0.0;
-	    double x2 = surface->width, y2 = surface->height;
-	    _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
-						  &x1, &y1, &x2, &y2,
-						  NULL);
-
-	    /* Rather than computing precise bounds of the union, just
-	     * add the surface extents unconditionally. We only
-	     * required an answer that's large enough, we don't really
-	     * care if it's not as tight as possible.*/
-	    xstep = ystep = ceil ((x2 - x1) + (y2 - y1) +
-				  image->width + image->height);
-	    break;
-	}
-	case CAIRO_EXTEND_REPEAT:
-	case CAIRO_EXTEND_REFLECT:
-	    xstep = image->width;
-	    ystep = image->height;
-	    break;
+    case CAIRO_EXTEND_PAD:
+    case CAIRO_EXTEND_NONE:
+    {
+	/* In PS/PDF, (as far as I can tell), all patterns are
+	 * repeating. So we support cairo's EXTEND_NONE semantics
+	 * by setting the repeat step size to a size large enough
+	 * to guarantee that no more than a single occurrence will
+	 * be visible.
+	 *
+	 * First, map the surface extents into pattern space (since
+	 * xstep and ystep are in pattern space).  Then use an upper
+	 * bound on the length of the diagonal of the pattern image
+	 * and the surface as repeat size.  This guarantees to never
+	 * repeat visibly.
+	 */
+	double x1 = 0.0, y1 = 0.0;
+	double x2 = surface->width, y2 = surface->height;
+	_cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+					      &x1, &y1, &x2, &y2,
+					      NULL);
+
+	/* Rather than computing precise bounds of the union, just
+	 * add the surface extents unconditionally. We only
+	 * required an answer that's large enough, we don't really
+	 * care if it's not as tight as possible.*/
+	xstep = ystep = ceil ((x2 - x1) + (y2 - y1) +
+			      pattern_width + pattern_height);
+	break;
+    }
+    case CAIRO_EXTEND_REPEAT:
+    case CAIRO_EXTEND_REFLECT:
+	xstep = pattern_width;
+	ystep = pattern_height;
+	break;
 	/* All the rest (if any) should have been analyzed away, so these
 	 * cases should be unreachable. */
-	default:
-	    ASSERT_NOT_REACHED;
-	    xstep = 0;
-	    ystep = 0;
-	}
-
-	_cairo_surface_release_source_image (pattern->surface,
-		                             image, image_extra);
+    default:
+	ASSERT_NOT_REACHED;
+	xstep = 0;
+	ystep = 0;
     }
+
     _cairo_output_stream_printf (surface->stream,
 				 "<< /PatternType 1\n"
 				 "   /PaintType 1\n"
 				 "   /TilingType 1\n");
     _cairo_output_stream_printf (surface->stream,
-				 "   /BBox [0 0 %f %f]\n",
-				 bbox_width, bbox_height);
+				 "   /BBox [0 0 %d %d]\n",
+				 pattern_width, pattern_height);
     _cairo_output_stream_printf (surface->stream,
 				 "   /XStep %f /YStep %f\n",
 				 xstep, ystep);
     _cairo_output_stream_printf (surface->stream,
-				 "   /PaintProc { MyPattern } bind\n"
+				 "   /PaintProc { CairoPattern } bind\n"
 				 ">>\n");
     _cairo_output_stream_printf (surface->stream,
 				 "[ %f %f %f %f %f %f ]\n",
@@ -2527,7 +2620,7 @@ _cairo_ps_surface_set_bounding_box (void
 
 static const cairo_surface_backend_t cairo_ps_surface_backend = {
     CAIRO_SURFACE_TYPE_PS,
-    NULL, /* create_similar */
+    _cairo_ps_surface_create_similar,
     _cairo_ps_surface_finish,
     NULL, /* acquire_source_image */
     NULL, /* release_source_image */


More information about the cairo-commit mailing list