[cairo-commit] 3 commits - boilerplate/cairo-boilerplate-pdf.c src/cairo-pdf-surface.c src/cairo-pdf-surface-private.h test/gradient-alpha.c test/linear-gradient.c test/text-pattern.c test/trap-clip.c

Carl Worth cworth at kemper.freedesktop.org
Fri Apr 27 16:41:32 PDT 2007


 boilerplate/cairo-boilerplate-pdf.c |   15 
 src/cairo-pdf-surface-private.h     |   14 
 src/cairo-pdf-surface.c             | 1005 +++++++++++++++++++++++++++---------
 test/gradient-alpha.c               |    3 
 test/linear-gradient.c              |    3 
 test/text-pattern.c                 |    3 
 test/trap-clip.c                    |    3 
 7 files changed, 802 insertions(+), 244 deletions(-)

New commits:
diff-tree 2dcfb944b044172640a3784246d6b3129b686b60 (from 179e3399101b5a0daa907dcfe1f9f11ae8fe691e)
Author: Carl Worth <cworth at cworth.org>
Date:   Fri Apr 27 16:41:17 2007 -0700

    Don't test PDF tests known to fail due to poppler limitations
    
    The following four tests are disabled:
    
    	gradient-alpha, linear-gradient, text-pattern, trap-clip
    
    We don't use XFAIL as that would disable all backends, (but
    we can still usefully use these tests on backends other than
    PDF).

diff --git a/boilerplate/cairo-boilerplate-pdf.c b/boilerplate/cairo-boilerplate-pdf.c
index e0a610e..0d8ac1a 100644
--- a/boilerplate/cairo-boilerplate-pdf.c
+++ b/boilerplate/cairo-boilerplate-pdf.c
@@ -42,6 +42,14 @@ typedef struct _pdf_target_closure
     cairo_surface_t	*target;
 } pdf_target_closure_t;
 
+#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
+static const char *pdf_ignored_tests[] = {
+    "gradient-alpha",
+    "linear-gradient",
+    "text-pattern",
+    "trap-clip"
+};
+
 cairo_surface_t *
 _cairo_boilerplate_pdf_create_surface (const char		 *name,
 				       cairo_content_t		  content,
@@ -53,6 +61,13 @@ _cairo_boilerplate_pdf_create_surface (c
     pdf_target_closure_t *ptc;
     cairo_surface_t *surface;
 
+    if (mode == CAIRO_BOILERPLATE_MODE_TEST) {
+	int i;
+	for (i = 0; i < ARRAY_LENGTH (pdf_ignored_tests); i++)
+	    if (strcmp (name, pdf_ignored_tests[i]) == 0)
+		return NULL;
+    }
+
     /* Sanitize back to a real cairo_content_t value. */
     if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
 	content = CAIRO_CONTENT_COLOR_ALPHA;
diff --git a/test/gradient-alpha.c b/test/gradient-alpha.c
index a56b507..6646aec 100644
--- a/test/gradient-alpha.c
+++ b/test/gradient-alpha.c
@@ -29,7 +29,8 @@ static cairo_test_draw_function_t draw;
 
 cairo_test_t test = {
     "gradient-alpha",
-    "Tests drawing of a gradient with various alpha values in the color stops",
+    "Tests drawing of a gradient with various alpha values in the color stops\n"
+    "PDF backend will not be tested due to limitations in poppler.",
     10, 10,
     draw
 };
diff --git a/test/linear-gradient.c b/test/linear-gradient.c
index 7681cbf..71a57f6 100644
--- a/test/linear-gradient.c
+++ b/test/linear-gradient.c
@@ -58,7 +58,8 @@ static cairo_test_draw_function_t draw;
 
 cairo_test_t test = {
     "linear-gradient",
-    "Tests the drawing of linear gradients",
+    "Tests the drawing of linear gradients\n"
+    "PDF backend will not be tested due to limitations in poppler.",
     WIDTH, HEIGHT,
     draw
 };
diff --git a/test/text-pattern.c b/test/text-pattern.c
index 02feb22..99ac9fd 100644
--- a/test/text-pattern.c
+++ b/test/text-pattern.c
@@ -32,7 +32,8 @@ static cairo_test_draw_function_t draw;
 
 cairo_test_t test = {
     "text-pattern",
-    "Patterned Text",
+    "Patterned Text\n"
+    "PDF backend will not be tested due to limitations in poppler.",
     IMAGE_WIDTH, IMAGE_HEIGHT,
     draw
 };
diff --git a/test/trap-clip.c b/test/trap-clip.c
index ba4d148..a33985f 100644
--- a/test/trap-clip.c
+++ b/test/trap-clip.c
@@ -168,7 +168,8 @@ static cairo_test_draw_function_t draw;
 
 cairo_test_t test = {
     "trap-clip",
-    "Trapezoid clipping",
+    "Trapezoid clipping\n"
+    "PDF backend will not be tested due to limitations in poppler.",
     IMAGE_WIDTH, IMAGE_HEIGHT,
     draw
 };
diff-tree 179e3399101b5a0daa907dcfe1f9f11ae8fe691e (from 1816d7c590ae4d423f9314a8a7794e2cae854da3)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri Apr 27 16:01:56 2007 -0700

    PDF: Change CTM to identity
    
    Some PDF viewers forget the CTM when drawing gradient patterns
    with SMasks. This patch works around these bugs by using the default
    identity matrix for the CTM. All paths are transformed from
    cairo to pdf coordinates before writing to the pdf file.

diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index 31ebc26..1af2ad0 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -58,6 +58,7 @@ struct _cairo_pdf_surface {
 
     double width;
     double height;
+    cairo_matrix_t cairo_to_pdf;
 
     cairo_array_t objects;
     cairo_array_t pages;
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 3398654..00dcd7c 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -245,6 +245,7 @@ _cairo_pdf_surface_create_for_stream_int
 
     surface->width = width;
     surface->height = height;
+    cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height);
 
     _cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t));
     _cairo_array_init (&surface->pages, sizeof (cairo_pdf_resource_t));
@@ -663,10 +664,6 @@ _cairo_pdf_surface_start_page (void *abs
     if (status)
 	return status;
 
-    _cairo_output_stream_printf (surface->output,
-				 "1 0 0 -1 0 %f cm\r\n",
-				 surface->height);
-
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -1422,8 +1419,8 @@ _cairo_pdf_surface_emit_linear_pattern (
 {
     cairo_pdf_resource_t pattern_resource, smask;
     cairo_pdf_resource_t color_function, alpha_function;
-    double x0, y0, x1, y1;
-    cairo_matrix_t p2u;
+    double x1, y1, x2, y2;
+    cairo_matrix_t pat_to_pdf;
     cairo_extend_t extend;
     cairo_status_t status;
 
@@ -1439,32 +1436,33 @@ _cairo_pdf_surface_emit_linear_pattern (
     if (status)
 	return status;
 
-    p2u = pattern->base.base.matrix;
-    status = cairo_matrix_invert (&p2u);
+    pat_to_pdf = pattern->base.base.matrix;
+    status = cairo_matrix_invert (&pat_to_pdf);
     if (status)
 	return status;
 
-    x0 = _cairo_fixed_to_double (pattern->gradient.p1.x);
-    y0 = _cairo_fixed_to_double (pattern->gradient.p1.y);
-    cairo_matrix_transform_point (&p2u, &x0, &y0);
-    x1 = _cairo_fixed_to_double (pattern->gradient.p2.x);
-    y1 = _cairo_fixed_to_double (pattern->gradient.p2.y);
-    cairo_matrix_transform_point (&p2u, &x1, &y1);
+    cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
+    x1 = _cairo_fixed_to_double (pattern->gradient.p1.x);
+    y1 = _cairo_fixed_to_double (pattern->gradient.p1.y);
+    x2 = _cairo_fixed_to_double (pattern->gradient.p2.x);
+    y2 = _cairo_fixed_to_double (pattern->gradient.p2.y);
 
     pattern_resource = _cairo_pdf_surface_new_object (surface);
     _cairo_output_stream_printf (surface->output,
                                  "%d 0 obj\r\n"
                                  "<< /Type /Pattern\r\n"
                                  "   /PatternType 2\r\n"
-                                 "   /Matrix [ 1 0 0 -1 0 %f ]\r\n"
+                                 "   /Matrix [ %f %f %f %f %f %f ]\r\n"
                                  "   /Shading\r\n"
                                  "      << /ShadingType 2\r\n"
                                  "         /ColorSpace /DeviceRGB\r\n"
                                  "         /Coords [ %f %f %f %f ]\r\n"
                                  "         /Function %d 0 R\r\n",
                                  pattern_resource.id,
-                                 surface->height,
-                                 x0, y0, x1, y1,
+                                 pat_to_pdf.xx, pat_to_pdf.yx,
+                                 pat_to_pdf.xy, pat_to_pdf.yy,
+                                 pat_to_pdf.x0, pat_to_pdf.y0,
+                                 x1, y1, x2, y2,
                                  color_function.id);
 
     if (extend == CAIRO_EXTEND_PAD) {
@@ -1491,15 +1489,17 @@ _cairo_pdf_surface_emit_linear_pattern (
                                      "%d 0 obj\r\n"
                                      "<< /Type /Pattern\r\n"
                                      "   /PatternType 2\r\n"
-                                     "   /Matrix [ 1 0 0 -1 0 %f ]\r\n"
+                                     "   /Matrix [ %f %f %f %f %f %f ]\r\n"
                                      "   /Shading\r\n"
                                      "      << /ShadingType 2\r\n"
                                      "         /ColorSpace /DeviceGray\r\n"
                                      "         /Coords [ %f %f %f %f ]\r\n"
                                      "         /Function %d 0 R\r\n",
                                      mask_resource.id,
-                                     surface->height,
-                                     x0, y0, x1, y1,
+                                     pat_to_pdf.xx, pat_to_pdf.yx,
+                                     pat_to_pdf.xy, pat_to_pdf.yy,
+                                     pat_to_pdf.x0, pat_to_pdf.y0,
+                                     x1, y1, x2, y2,
                                      alpha_function.id);
 
         if (extend == CAIRO_EXTEND_PAD) {
@@ -1541,8 +1541,8 @@ _cairo_pdf_surface_emit_radial_pattern (
 {
     cairo_pdf_resource_t pattern_resource, smask;
     cairo_pdf_resource_t color_function, alpha_function;
-    double x0, y0, x1, y1, r0, r1;
-    cairo_matrix_t p2u, pdf_p2d;
+    double x1, y1, x2, y2, r1, r2;
+    cairo_matrix_t pat_to_pdf;
     cairo_extend_t extend;
     cairo_status_t status;
 
@@ -1558,22 +1558,18 @@ _cairo_pdf_surface_emit_radial_pattern (
     if (status)
 	return status;
 
-    p2u = pattern->base.base.matrix;
-    status = cairo_matrix_invert (&p2u);
+    pat_to_pdf = pattern->base.base.matrix;
+    status = cairo_matrix_invert (&pat_to_pdf);
     if (status)
 	return status;
 
-    x0 = _cairo_fixed_to_double (pattern->gradient.c1.x);
-    y0 = _cairo_fixed_to_double (pattern->gradient.c1.y);
-    r0 = _cairo_fixed_to_double (pattern->gradient.c1.radius);
-    x1 = _cairo_fixed_to_double (pattern->gradient.c2.x);
-    y1 = _cairo_fixed_to_double (pattern->gradient.c2.y);
-    r1 = _cairo_fixed_to_double (pattern->gradient.c2.radius);
-
-    cairo_matrix_init_identity (&pdf_p2d);
-    cairo_matrix_translate (&pdf_p2d, 0.0, surface->height);
-    cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
-    cairo_matrix_multiply (&pdf_p2d, &p2u, &pdf_p2d);
+    cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
+    x1 = _cairo_fixed_to_double (pattern->gradient.c1.x);
+    y1 = _cairo_fixed_to_double (pattern->gradient.c1.y);
+    r1 = _cairo_fixed_to_double (pattern->gradient.c1.radius);
+    x2 = _cairo_fixed_to_double (pattern->gradient.c2.x);
+    y2 = _cairo_fixed_to_double (pattern->gradient.c2.y);
+    r2 = _cairo_fixed_to_double (pattern->gradient.c2.radius);
 
     pattern_resource = _cairo_pdf_surface_new_object (surface);
     _cairo_output_stream_printf (surface->output,
@@ -1587,10 +1583,10 @@ _cairo_pdf_surface_emit_radial_pattern (
                                  "         /Coords [ %f %f %f %f %f %f ]\r\n"
                                  "         /Function %d 0 R\r\n",
                                  pattern_resource.id,
-                                 pdf_p2d.xx, pdf_p2d.yx,
-                                 pdf_p2d.xy, pdf_p2d.yy,
-                                 pdf_p2d.x0, pdf_p2d.y0,
-                                 x0, y0, r0, x1, y1, r1,
+                                 pat_to_pdf.xx, pat_to_pdf.yx,
+                                 pat_to_pdf.xy, pat_to_pdf.yy,
+                                 pat_to_pdf.x0, pat_to_pdf.y0,
+                                 x1, y1, r1, x2, y2, r2,
                                  color_function.id);
 
     if (extend == CAIRO_EXTEND_PAD) {
@@ -1624,10 +1620,10 @@ _cairo_pdf_surface_emit_radial_pattern (
                                      "         /Coords [ %f %f %f %f %f %f ]\r\n"
                                      "         /Function %d 0 R\r\n",
                                      mask_resource.id,
-                                     pdf_p2d.xx, pdf_p2d.yx,
-                                     pdf_p2d.xy, pdf_p2d.yy,
-                                     pdf_p2d.x0, pdf_p2d.y0,
-                                     x0, y0, r0, x1, y1, r1,
+                                     pat_to_pdf.xx, pat_to_pdf.yx,
+                                     pat_to_pdf.xy, pat_to_pdf.yy,
+                                     pat_to_pdf.x0, pat_to_pdf.y0,
+                                     x1, y1, r1, x2, y2, r2,
                                      alpha_function.id);
 
         if (extend == CAIRO_EXTEND_PAD) {
@@ -1767,6 +1763,7 @@ _cairo_pdf_surface_get_extents (void		  
 
 typedef struct _pdf_path_info {
     cairo_output_stream_t   *output;
+    cairo_matrix_t	    *cairo_to_pdf;
     cairo_matrix_t	    *ctm_inverse;
 } pdf_path_info_t;
 
@@ -1777,6 +1774,8 @@ _cairo_pdf_path_move_to (void *closure, 
     double x = _cairo_fixed_to_double (point->x);
     double y = _cairo_fixed_to_double (point->y);
 
+    if (info->cairo_to_pdf)
+        cairo_matrix_transform_point (info->cairo_to_pdf, &x, &y);
     if (info->ctm_inverse)
 	cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
 
@@ -1793,6 +1792,8 @@ _cairo_pdf_path_line_to (void *closure, 
     double x = _cairo_fixed_to_double (point->x);
     double y = _cairo_fixed_to_double (point->y);
 
+    if (info->cairo_to_pdf)
+        cairo_matrix_transform_point (info->cairo_to_pdf, &x, &y);
     if (info->ctm_inverse)
 	cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
 
@@ -1815,6 +1816,11 @@ _cairo_pdf_path_curve_to (void          
     double dx = _cairo_fixed_to_double (d->x);
     double dy = _cairo_fixed_to_double (d->y);
 
+    if (info->cairo_to_pdf) {
+        cairo_matrix_transform_point (info->cairo_to_pdf, &bx, &by);
+        cairo_matrix_transform_point (info->cairo_to_pdf, &cx, &cy);
+        cairo_matrix_transform_point (info->cairo_to_pdf, &dx, &dy);
+    }
     if (info->ctm_inverse) {
 	cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
 	cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
@@ -1863,6 +1869,7 @@ _cairo_pdf_surface_intersect_clip_path (
     }
 
     info.output = surface->output;
+    info.cairo_to_pdf = &surface->cairo_to_pdf;
     info.ctm_inverse = NULL;
 
     status = _cairo_path_fixed_interpret (path,
@@ -2581,6 +2588,7 @@ _cairo_pdf_surface_emit_outline_glyph (c
 				 -_cairo_fixed_to_double (scaled_glyph->bbox.p1.y));
 
     info.output = surface->output;
+    info.cairo_to_pdf = &surface->cairo_to_pdf;
     info.ctm_inverse = NULL;
 
     status = _cairo_path_fixed_interpret (scaled_glyph->path,
@@ -3143,9 +3151,6 @@ _cairo_pdf_surface_paint (void			*abstra
 	status = _cairo_pdf_surface_begin_group (surface, &group);
         if (status)
             return status;
-        _cairo_output_stream_printf (surface->output,
-                                     "1 0 0 -1 0 %f cm\r\n",
-                                     surface->height);
     } else {
         _cairo_output_stream_printf (surface->output, "q ");
     }
@@ -3160,8 +3165,7 @@ _cairo_pdf_surface_paint (void			*abstra
         _cairo_pdf_surface_end_group (surface);
 
         _cairo_output_stream_printf (surface->output,
-                                     "q 1 0 0 -1 0 %f cm /sm%d gs /res%d Do Q\r\n",
-                                     surface->height,
+                                     "q /sm%d gs /res%d Do Q\r\n",
                                      surface->emitted_pattern.smask,
                                      group.id);
     } else {
@@ -3268,6 +3272,7 @@ _cairo_pdf_surface_stroke (void			*abstr
     cairo_pdf_resource_t group = {0}; /* squelch bogus compiler warning */
     pdf_path_info_t info;
     cairo_status_t status;
+    cairo_matrix_t m;
 
     if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
 	return _cairo_pdf_surface_analyze_operation (surface, op, source);
@@ -3282,9 +3287,6 @@ _cairo_pdf_surface_stroke (void			*abstr
 	status = _cairo_pdf_surface_begin_group (surface, &group);
         if (status)
             return status;
-        _cairo_output_stream_printf (surface->output,
-                                     "1 0 0 -1 0 %f cm\r\n",
-                                     surface->height);
     } else {
         _cairo_output_stream_printf (surface->output, "q ");
     }
@@ -3297,12 +3299,14 @@ _cairo_pdf_surface_stroke (void			*abstr
 	return status;
 
     info.output = surface->output;
+    info.cairo_to_pdf = NULL;
     info.ctm_inverse = ctm_inverse;
 
+    cairo_matrix_multiply (&m, ctm, &surface->cairo_to_pdf);
     _cairo_output_stream_printf (surface->output,
 				 "q %f %f %f %f %f %f cm\r\n",
-				 ctm->xx, ctm->yx, ctm->xy, ctm->yy,
-				 ctm->x0, ctm->y0);
+				 m.xx, m.yx, m.xy, m.yy,
+				 m.x0, m.y0);
 
     status = _cairo_path_fixed_interpret (path,
 					  CAIRO_DIRECTION_FORWARD,
@@ -3318,8 +3322,7 @@ _cairo_pdf_surface_stroke (void			*abstr
         _cairo_pdf_surface_end_group (surface);
 
         _cairo_output_stream_printf (surface->output,
-                                     "q 1 0 0 -1 0 %f cm /sm%d gs /res%d Do Q\r\n",
-                                     surface->height,
+                                     "q /sm%d gs /res%d Do Q\r\n",
                                      surface->emitted_pattern.smask,
                                      group.id);
     } else {
@@ -3357,15 +3360,13 @@ _cairo_pdf_surface_fill (void			*abstrac
 	status = _cairo_pdf_surface_begin_group (surface, &group);
         if (status)
             return status;
-        _cairo_output_stream_printf (surface->output,
-                                     "1 0 0 -1 0 %f cm\r\n",
-                                     surface->height);
     } else {
         _cairo_output_stream_printf (surface->output, "q ");
     }
 
     _cairo_pdf_surface_select_pattern (surface, FALSE);
     info.output = surface->output;
+    info.cairo_to_pdf = &surface->cairo_to_pdf;
     info.ctm_inverse = NULL;
     status = _cairo_path_fixed_interpret (path,
 					  CAIRO_DIRECTION_FORWARD,
@@ -3394,8 +3395,7 @@ _cairo_pdf_surface_fill (void			*abstrac
         _cairo_pdf_surface_end_group (surface);
 
         _cairo_output_stream_printf (surface->output,
-                                     "q 1 0 0 -1 0 %f cm /sm%d gs /res%d Do Q\r\n",
-                                     surface->height,
+                                     "q /sm%d gs /res%d Do Q\r\n",
                                      surface->emitted_pattern.smask,
                                      group.id);
     } else {
@@ -3438,9 +3438,6 @@ _cairo_pdf_surface_show_glyphs (void			*
 	status = _cairo_pdf_surface_begin_group (surface, &group);
         if (status)
             return status;
-        _cairo_output_stream_printf (surface->output,
-                                     "1 0 0 -1 0 %f cm\r\n",
-                                     surface->height);
     } else {
         _cairo_output_stream_printf (surface->output, "q ");
     }
@@ -3491,11 +3488,11 @@ _cairo_pdf_surface_show_glyphs (void			*
             _cairo_output_stream_printf (surface->output,
                                          "%f %f %f %f %f %f Tm\r\n",
                                          scaled_font->scale.xx,
-                                         scaled_font->scale.yx,
+                                         -scaled_font->scale.yx,
                                          -scaled_font->scale.xy,
-                                         -scaled_font->scale.yy,
+                                         scaled_font->scale.yy,
                                          glyphs[i].x,
-                                         glyphs[i].y);
+                                         surface->height - glyphs[i].y);
             current_subset_id = subset_glyph.subset_id;
             Tlm_x = glyphs[i].x;
             Tlm_y = glyphs[i].y;
@@ -3512,7 +3509,8 @@ _cairo_pdf_surface_show_glyphs (void			*
                         _cairo_output_stream_printf (surface->output,
                                                      "%f %f Td\r\n",
                                                      (glyphs[i].x - Tlm_x)/scaled_font->scale.xx,
-                                                     (glyphs[i].y - Tlm_y)/-scaled_font->scale.yy);
+                                                     -(glyphs[i].y - Tlm_y)/scaled_font->scale.yy);
+
                         Tlm_x = glyphs[i].x;
                         Tlm_y = glyphs[i].y;
                         Tm_x = Tlm_x;
@@ -3588,8 +3586,7 @@ _cairo_pdf_surface_show_glyphs (void			*
         _cairo_pdf_surface_end_group (surface);
 
         _cairo_output_stream_printf (surface->output,
-                                     "q 1 0 0 -1 0 %f cm /sm%d gs /res%d Do Q\r\n",
-                                     surface->height,
+                                     "q /sm%d gs /res%d Do Q\r\n",
                                      surface->emitted_pattern.smask,
                                      group.id);
     } else {
diff-tree 1816d7c590ae4d423f9314a8a7794e2cae854da3 (from 050dad71734739f1eaf8976ca5bb88e47c76409c)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri Apr 27 09:49:45 2007 -0700

    Add support for transparent gradients
    
    This is based on the gradient patch written by Miklós Erdélyi at
    http://lists.freedesktop.org/archives/cairo/2006-August/007648.html
    
    Currently only EXTEND_NONE and EXTEND_PAD are supported. Other extend
    types will go through the image fallback path.

diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index 1fcf774..31ebc26 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -65,6 +65,9 @@ struct _cairo_pdf_surface {
     cairo_array_t xobjects;
     cairo_array_t streams;
     cairo_array_t alphas;
+    cairo_array_t smasks;
+    cairo_array_t rgb_linear_functions;
+    cairo_array_t alpha_linear_functions;
 
     cairo_scaled_font_subsets_t *font_subsets;
     cairo_array_t fonts;
@@ -81,6 +84,16 @@ struct _cairo_pdf_surface {
         cairo_output_stream_t *old_output;
     } current_stream;
 
+    struct {
+        cairo_pattern_type_t type;
+        double red;
+        double green;
+        double blue;
+        int alpha;
+        cairo_pdf_resource_t smask;
+        cairo_pdf_resource_t pattern;
+    } emitted_pattern;
+
     cairo_bool_t has_clip;
 
     cairo_paginated_mode_t paginated_mode;
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 073191e..3398654 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -64,8 +64,6 @@
  *
  * - Surface patterns.
  *
- * - Alpha channels in gradients.
- *
  * - Should/does cairo support drawing into a scratch surface and then
  *   using that as a fill pattern?  For this backend, that would involve
  *   using a tiling pattern (4.6.2).  How do you create such a scratch
@@ -97,6 +95,18 @@ typedef struct _cairo_pdf_font {
     cairo_pdf_resource_t subset_resource;
 } cairo_pdf_font_t;
 
+typedef struct _cairo_pdf_rgb_linear_function {
+    cairo_pdf_resource_t resource;
+    double               color1[3];
+    double               color2[3];
+} cairo_pdf_rgb_linear_function_t;
+
+typedef struct _cairo_pdf_alpha_linear_function {
+    cairo_pdf_resource_t resource;
+    double               alpha1;
+    double               alpha2;
+} cairo_pdf_alpha_linear_function_t;
+
 static cairo_pdf_resource_t
 _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface);
 
@@ -182,28 +192,33 @@ _cairo_pdf_surface_add_pattern (cairo_pd
 }
 
 static cairo_status_t
-_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface,
-			      double alpha,
-			      cairo_pdf_resource_t *resource)
+_cairo_pdf_surface_add_smask (cairo_pdf_surface_t  *surface,
+                              cairo_pdf_resource_t  smask)
+{
+    return _cairo_array_append (&surface->smasks, &smask);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha, int *index)
 {
-    cairo_status_t status;
     int num_alphas, i;
     double other;
+    cairo_status_t status;
 
     num_alphas = _cairo_array_num_elements (&surface->alphas);
     for (i = 0; i < num_alphas; i++) {
-	_cairo_array_copy_element (&surface->alphas, i, &other);
-	if (alpha == other) {
-	    resource->id  = i;
-	    return CAIRO_STATUS_SUCCESS;
-	}
+        _cairo_array_copy_element (&surface->alphas, i, &other);
+        if (alpha == other) {
+            *index = i;
+            return CAIRO_STATUS_SUCCESS;
+        }
     }
 
     status = _cairo_array_append (&surface->alphas, &alpha);
     if (status)
 	return status;
 
-    resource->id = _cairo_array_num_elements (&surface->alphas) - 1;
+    *index = _cairo_array_num_elements (&surface->alphas) - 1;
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -214,6 +229,8 @@ _cairo_pdf_surface_create_for_stream_int
 					       double			 height)
 {
     cairo_pdf_surface_t *surface;
+    cairo_status_t status;
+    int alpha;
 
     surface = malloc (sizeof (cairo_pdf_surface_t));
     if (surface == NULL) {
@@ -235,12 +252,23 @@ _cairo_pdf_surface_create_for_stream_int
     _cairo_array_init (&surface->xobjects, sizeof (cairo_pdf_resource_t));
     _cairo_array_init (&surface->streams, sizeof (cairo_pdf_resource_t));
     _cairo_array_init (&surface->alphas, sizeof (double));
+    _cairo_array_init (&surface->smasks, sizeof (cairo_pdf_resource_t));
+    _cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t));
+    _cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t));
+
+
+    /* Add alpha=1 as the first element in the list of alpha values as
+     * this is the most frequently referenced value. */
+    status = _cairo_pdf_surface_add_alpha (surface, 1, &alpha);
+    if (status) {
+	_cairo_error (status);
+        goto fail1;
+    }
 
     surface->font_subsets = _cairo_scaled_font_subsets_create_composite ();
     if (! surface->font_subsets) {
 	_cairo_error (CAIRO_STATUS_NO_MEMORY);
-	free (surface);
-	return (cairo_surface_t*) &_cairo_surface_nil;
+        goto fail2;
     }
 
     _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t));
@@ -266,6 +294,11 @@ _cairo_pdf_surface_create_for_stream_int
 					    CAIRO_CONTENT_COLOR_ALPHA,
 					    width, height,
 					    &cairo_pdf_surface_paginated_backend);
+
+fail1:
+    free (surface);
+fail2:
+	return (cairo_surface_t*) &_cairo_surface_nil;
 }
 
 /**
@@ -542,6 +575,9 @@ _cairo_pdf_surface_finish (void *abstrac
     _cairo_array_fini (&surface->xobjects);
     _cairo_array_fini (&surface->streams);
     _cairo_array_fini (&surface->alphas);
+    _cairo_array_fini (&surface->smasks);
+    _cairo_array_fini (&surface->rgb_linear_functions);
+    _cairo_array_fini (&surface->alpha_linear_functions);
 
     if (surface->font_subsets) {
 	_cairo_scaled_font_subsets_destroy (surface->font_subsets);
@@ -575,6 +611,39 @@ _cairo_pdf_surface_resume_content_stream
     return _cairo_pdf_surface_add_stream (surface, stream);
 }
 
+static cairo_status_t
+_cairo_pdf_surface_begin_group (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *group)
+{
+    cairo_pdf_resource_t g;
+    cairo_status_t status;
+
+    _cairo_pdf_surface_pause_content_stream (surface);
+    g = _cairo_pdf_surface_open_stream (surface,
+                                        TRUE,
+                                        "   /Type /XObject\r\n"
+                                        "   /Subtype /Form\r\n"
+                                        "   /BBox [ 0 0 %f %f ]\r\n"
+                                        "   /Group <<\r\n"
+                                        "      /Type /Group\r\n"
+                                        "      /S /Transparency\r\n"
+                                        "      /CS /DeviceRGB\r\n"
+                                        "   >>\r\n",
+                                        surface->width,
+                                        surface->height);
+
+    status = _cairo_array_append (&surface->xobjects, &g);
+    *group = g;
+
+    return status;
+}
+
+static void
+_cairo_pdf_surface_end_group (cairo_pdf_surface_t *surface)
+{
+    _cairo_pdf_surface_close_stream (surface);
+    _cairo_pdf_surface_resume_content_stream (surface);
+}
+
 static cairo_int_status_t
 _cairo_pdf_surface_start_page (void *abstract_surface)
 {
@@ -696,9 +765,9 @@ _cairo_pdf_surface_emit_smask (cairo_pdf
 /* Emit image data into the given surface, providing a resource that
  * can be used to reference the data in image_ret. */
 static cairo_status_t
-_cairo_pdf_surface_emit_image (cairo_pdf_surface_t		*surface,
-	    cairo_image_surface_t	*image,
-	    cairo_pdf_resource_t	*image_ret)
+_cairo_pdf_surface_emit_image (cairo_pdf_surface_t   *surface,
+                               cairo_image_surface_t *image,
+                               cairo_pdf_resource_t  *image_ret)
 {
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
     char *rgb, *compressed;
@@ -809,38 +878,30 @@ _cairo_pdf_surface_emit_image (cairo_pdf
 }
 
 static cairo_status_t
-_cairo_pdf_surface_emit_solid_pattern (cairo_pdf_surface_t *surface,
-		    cairo_solid_pattern_t *pattern)
+_cairo_pdf_surface_emit_solid_pattern (cairo_pdf_surface_t   *surface,
+                                       cairo_solid_pattern_t *pattern)
 {
+    int alpha;
     cairo_status_t status;
-    cairo_pdf_resource_t alpha;
 
-    status = _cairo_pdf_surface_add_alpha (surface,
-					   pattern->color.alpha,
-					   &alpha);
+    status = _cairo_pdf_surface_add_alpha (surface, pattern->color.alpha, &alpha);
     if (status)
 	return status;
 
-    /* With some work, we could separate the stroking
-     * or non-stroking color here as actually needed. */
-    _cairo_output_stream_printf (surface->output,
-				 "%f %f %f RG "
-				 "%f %f %f rg "
-				 "/a%d gs\r\n",
-				 pattern->color.red,
-				 pattern->color.green,
-				 pattern->color.blue,
-				 pattern->color.red,
-				 pattern->color.green,
-				 pattern->color.blue,
-				 alpha.id);
+    surface->emitted_pattern.type = CAIRO_PATTERN_TYPE_SOLID;
+    surface->emitted_pattern.red = pattern->color.red;
+    surface->emitted_pattern.green = pattern->color.green;
+    surface->emitted_pattern.blue = pattern->color.blue;
+    surface->emitted_pattern.alpha = alpha;
+    surface->emitted_pattern.smask.id = 0;
+    surface->emitted_pattern.pattern.id = 0;
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_status_t
-_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t	*surface,
-		      cairo_surface_pattern_t	*pattern)
+_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t	 *surface,
+                                         cairo_surface_pattern_t *pattern)
 {
     cairo_pdf_resource_t stream;
     cairo_surface_t *pat_surface;
@@ -848,7 +909,7 @@ _cairo_pdf_surface_emit_surface_pattern 
     cairo_image_surface_t *image;
     void *image_extra;
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
-    cairo_pdf_resource_t alpha, image_resource = {0}; /* squelch bogus compiler warning */
+    cairo_pdf_resource_t image_resource = {0}; /* squelch bogus compiler warning */
     cairo_matrix_t cairo_p2d, pdf_p2d;
     cairo_extend_t extend = cairo_pattern_get_extend (&pattern->base);
     double xstep, ystep;
@@ -997,17 +1058,12 @@ _cairo_pdf_surface_emit_surface_pattern 
     if (status)
 	goto BAIL;
 
-    status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+    status = _cairo_pdf_surface_add_alpha (surface, 1.0, &surface->emitted_pattern.alpha);
     if (status)
 	goto BAIL;
-
-    /* With some work, we could separate the stroking
-     * or non-stroking pattern here as actually needed. */
-    _cairo_output_stream_printf (surface->output,
-				 "/Pattern CS /res%d SCN "
-				 "/Pattern cs /res%d scn "
-				 "/a%d gs\r\n",
-				 stream.id, stream.id, alpha.id);
+    surface->emitted_pattern.type = CAIRO_PATTERN_TYPE_SURFACE;
+    surface->emitted_pattern.smask.id = 0;
+    surface->emitted_pattern.pattern = stream;
 
  BAIL:
     _cairo_surface_release_source_image (pat_surface, image, image_extra);
@@ -1018,90 +1074,168 @@ _cairo_pdf_surface_emit_surface_pattern 
 }
 
 typedef struct _cairo_pdf_color_stop {
-    double	  		offset;
-    cairo_pdf_resource_t	gradient;
-    unsigned char		color_char[4];
+    double offset;
+    double color[4];
+    cairo_pdf_resource_t resource;
 } cairo_pdf_color_stop_t;
 
-static cairo_pdf_resource_t
-_cairo_pdf_surface_emit_linear_colorgradient (cairo_pdf_surface_t		*surface,
-			   cairo_pdf_color_stop_t	*stop1,
-			   cairo_pdf_color_stop_t	*stop2)
+static cairo_status_t
+cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t    *surface,
+                                            cairo_pdf_color_stop_t *stop1,
+                                            cairo_pdf_color_stop_t *stop2,
+                                            cairo_pdf_resource_t   *function)
 {
-    cairo_pdf_resource_t function = _cairo_pdf_surface_new_object (surface);
+    int num_elems, i;
+    cairo_pdf_rgb_linear_function_t elem;
+    cairo_pdf_resource_t res;
+    cairo_status_t status;
+
+    num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions);
+    for (i = 0; i < num_elems; i++) {
+	_cairo_array_copy_element (&surface->rgb_linear_functions, i, &elem);
+        if (memcmp (&elem.color1[0], &stop1->color[0], sizeof (double)*3) != 0)
+            continue;
+        if (memcmp (&elem.color2[0], &stop2->color[0], sizeof (double)*3) != 0)
+            continue;
+        *function =  elem.resource;
+        return CAIRO_STATUS_SUCCESS;
+    }
+
+    res = _cairo_pdf_surface_new_object (surface);
 
     _cairo_output_stream_printf (surface->output,
 				 "%d 0 obj\r\n"
-				 "<< /FunctionType 0\r\n"
+				 "<< /FunctionType 2\r\n"
 				 "   /Domain [ 0 1 ]\r\n"
-				 "   /Size [ 2 ]\r\n"
-				 "   /BitsPerSample 8\r\n"
-				 "   /Range [ 0 1 0 1 0 1 ]\r\n"
-				 "   /Length 6\r\n"
+				 "   /C0 [ %f %f %f ]\r\n"
+				 "   /C1 [ %f %f %f ]\r\n"
+				 "   /N 1\r\n"
 				 ">>\r\n"
-				 "stream\r\n",
-				 function.id);
+				 "endobj\r\n",
+				 res.id,
+                                 stop1->color[0],
+                                 stop1->color[1],
+                                 stop1->color[2],
+                                 stop2->color[0],
+                                 stop2->color[1],
+                                 stop2->color[2]);
+
+    elem.resource = res;
+    memcpy (&elem.color1[0], &stop1->color[0], sizeof (double)*3);
+    memcpy (&elem.color2[0], &stop2->color[0], sizeof (double)*3);
+
+    status = _cairo_array_append (&surface->rgb_linear_functions, &elem);
+    *function = res;
+
+    return status;
+}
+
+static cairo_status_t
+cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t    *surface,
+                                              cairo_pdf_color_stop_t *stop1,
+                                              cairo_pdf_color_stop_t *stop2,
+                                              cairo_pdf_resource_t   *function)
+{
+    int num_elems, i;
+    cairo_pdf_alpha_linear_function_t elem;
+    cairo_pdf_resource_t res;
+    cairo_status_t status;
+
+    num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions);
+    for (i = 0; i < num_elems; i++) {
+	_cairo_array_copy_element (&surface->alpha_linear_functions, i, &elem);
+        if (elem.alpha1 != stop1->color[3])
+            continue;
+        if (elem.alpha2 != stop2->color[3])
+            continue;
+        *function =  elem.resource;
+        return CAIRO_STATUS_SUCCESS;
+    }
+
+    res = _cairo_pdf_surface_new_object (surface);
 
-    _cairo_output_stream_write (surface->output, stop1->color_char, 3);
-    _cairo_output_stream_write (surface->output, stop2->color_char, 3);
     _cairo_output_stream_printf (surface->output,
-				 "\r\n"
-				 "endstream\r\n"
-				 "endobj\r\n");
+				 "%d 0 obj\r\n"
+				 "<< /FunctionType 2\r\n"
+				 "   /Domain [ 0 1 ]\r\n"
+				 "   /C0 [ %f ]\r\n"
+				 "   /C1 [ %f ]\r\n"
+				 "   /N 1\r\n"
+				 ">>\r\n"
+				 "endobj\r\n",
+				 res.id,
+                                 stop1->color[3],
+                                 stop2->color[3]);
+
+    elem.resource = res;
+    elem.alpha1 = stop1->color[3];
+    elem.alpha2 = stop2->color[3];
+
+    status = _cairo_array_append (&surface->alpha_linear_functions, &elem);
+    *function = res;
 
-    return function;
+    return status;
 }
 
-static cairo_pdf_resource_t
-_cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t   *surface,
-			    unsigned int 	   n_stops,
-			    cairo_pdf_color_stop_t stops[])
+static cairo_status_t
+_cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t    *surface,
+                                                unsigned int 	        n_stops,
+                                                cairo_pdf_color_stop_t *stops,
+                                                cairo_bool_t	        is_alpha,
+                                                cairo_pdf_resource_t   *function)
 {
-    cairo_pdf_resource_t function;
+    cairo_pdf_resource_t res;
     unsigned int i;
+    cairo_status_t status;
 
     /* emit linear gradients between pairs of subsequent stops... */
     for (i = 0; i < n_stops-1; i++) {
-	stops[i].gradient = _cairo_pdf_surface_emit_linear_colorgradient (surface,
-						       &stops[i],
-						       &stops[i+1]);
+        if (is_alpha) {
+            status = cairo_pdf_surface_emit_alpha_linear_function (surface,
+                                                             &stops[i],
+                                                             &stops[i+1],
+                                                             &stops[i].resource);
+            if (status)
+                return status;
+        } else {
+            status = cairo_pdf_surface_emit_rgb_linear_function (surface,
+                                                                 &stops[i],
+                                                                 &stops[i+1],
+                                                                 &stops[i].resource);
+            if (status)
+                return status;
+        }
     }
 
     /* ... and stitch them together */
-    function = _cairo_pdf_surface_new_object (surface);
+    res = _cairo_pdf_surface_new_object (surface);
     _cairo_output_stream_printf (surface->output,
 				 "%d 0 obj\r\n"
 				 "<< /FunctionType 3\r\n"
 				 "   /Domain [ 0 1 ]\r\n",
-				 function.id);
+				 res.id);
 
     _cairo_output_stream_printf (surface->output,
 				 "   /Functions [ ");
     for (i = 0; i < n_stops-1; i++)
-    {
         _cairo_output_stream_printf (surface->output,
-				     "%d 0 R ", stops[i].gradient.id);
-    }
+                                     "%d 0 R ", stops[i].resource.id);
     _cairo_output_stream_printf (surface->output,
 		    		 "]\r\n");
 
     _cairo_output_stream_printf (surface->output,
 				 "   /Bounds [ ");
     for (i = 1; i < n_stops-1; i++)
-    {
         _cairo_output_stream_printf (surface->output,
 				     "%f ", stops[i].offset);
-    }
     _cairo_output_stream_printf (surface->output,
 		    		 "]\r\n");
 
     _cairo_output_stream_printf (surface->output,
 				 "   /Encode [ ");
     for (i = 1; i < n_stops; i++)
-    {
         _cairo_output_stream_printf (surface->output,
 				     "0 1 ");
-    }
     _cairo_output_stream_printf (surface->output,
 	    			 "]\r\n");
 
@@ -1109,34 +1243,43 @@ _cairo_pdf_surface_emit_stitched_colorgr
 	    			 ">>\r\n"
 				 "endobj\r\n");
 
-    return function;
+    *function = res;
+
+    return CAIRO_STATUS_SUCCESS;
 }
 
 #define COLOR_STOP_EPSILON 1e-6
 
-static cairo_pdf_resource_t
-_cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern)
+static  cairo_status_t
+_cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t      *surface,
+                                       cairo_gradient_pattern_t *pattern,
+                                       cairo_pdf_resource_t     *color_function,
+                                       cairo_pdf_resource_t     *alpha_function)
 {
-    cairo_pdf_resource_t    function;
     cairo_pdf_color_stop_t *allstops, *stops;
-    unsigned int i, n_stops;
+    unsigned int n_stops;
+    unsigned int i;
+    cairo_bool_t emit_alpha = FALSE;
+    cairo_status_t status;
 
-    function = _cairo_pdf_surface_new_object (surface);
+    color_function->id = 0;
+    alpha_function->id = 0;
 
     allstops = malloc ((pattern->n_stops + 2) * sizeof (cairo_pdf_color_stop_t));
     if (allstops == NULL) {
-	_cairo_error (CAIRO_STATUS_NO_MEMORY);
-	function.id = 0;
-	return function;
+	return CAIRO_STATUS_NO_MEMORY;
     }
+
     stops = &allstops[1];
     n_stops = pattern->n_stops;
 
-    for (i = 0; i < pattern->n_stops; i++) {
-	stops[i].color_char[0] = pattern->stops[i].color.red   >> 8;
-	stops[i].color_char[1] = pattern->stops[i].color.green >> 8;
-	stops[i].color_char[2] = pattern->stops[i].color.blue  >> 8;
-	stops[i].color_char[3] = pattern->stops[i].color.alpha >> 8;
+    for (i = 0; i < n_stops; i++) {
+	stops[i].color[0] = pattern->stops[i].color.red / 65535.0;
+	stops[i].color[1] = pattern->stops[i].color.green / 65535.0;
+	stops[i].color[2] = pattern->stops[i].color.blue / 65535.0;
+	stops[i].color[3] = pattern->stops[i].color.alpha / 65535.0;
+        if (!CAIRO_ALPHA_IS_OPAQUE (stops[i].color[3]))
+            emit_alpha = TRUE;
 	stops[i].offset = _cairo_fixed_to_double (pattern->stops[i].x);
     }
 
@@ -1145,48 +1288,156 @@ _cairo_pdf_surface_emit_pattern_stops (c
     if (stops[0].offset > COLOR_STOP_EPSILON) {
 	    memcpy (allstops, stops, sizeof (cairo_pdf_color_stop_t));
 	    stops = allstops;
-	    stops[0].offset = 0.0;
 	    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_pdf_color_stop_t));
-	    stops[n_stops].offset = 1.0;
 	    n_stops++;
     }
+    stops[n_stops].offset = 1.0;
 
     if (n_stops == 2) {
-	/* no need for stitched function */
-	function = _cairo_pdf_surface_emit_linear_colorgradient (surface, &stops[0], &stops[1]);
+        /* no need for stitched function */
+        status = cairo_pdf_surface_emit_rgb_linear_function (surface,
+                                                             &stops[0],
+                                                             &stops[1],
+                                                             color_function);
+        if (status)
+            return status;
+
+        if (emit_alpha) {
+            status = cairo_pdf_surface_emit_alpha_linear_function (surface,
+                                                                   &stops[0],
+                                                                   &stops[1],
+                                                                   alpha_function);
+            if (status)
+                return status;
+        }
     } else {
-	/* multiple stops: stitch. XXX possible optimization: regulary spaced
-	 * stops do not require stitching. XXX */
-	function = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
-					       n_stops,
-					       stops);
-    }
+        /* multiple stops: stitch. XXX possible optimization: regulary spaced
+         * stops do not require stitching. XXX */
+        status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+                                                                 n_stops,
+                                                                 stops,
+                                                                 FALSE,
+                                                                 color_function);
+        if (status)
+            return status;
 
+        if (emit_alpha) {
+            status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+                                                                     n_stops,
+                                                                     stops,
+                                                                     TRUE,
+                                                                     alpha_function);
+            if (status)
+                return status;
+        }
+    }
     free (allstops);
 
-    return function;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_pdf_resource_t
+cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t  *surface,
+                                           cairo_pdf_resource_t  gradient_mask)
+{
+    cairo_pdf_resource_t xobj_resource, smask_resource, gstate_resource;
+
+    xobj_resource = _cairo_pdf_surface_open_stream (surface,
+                                                    TRUE,
+                                                    "   /Type /XObject\r\n"
+                                                    "   /Subtype /Form\r\n"
+                                                    "   /FormType 1\r\n"
+                                                    "   /BBox [ 0 0 %f %f ]\r\n"
+                                                    "   /Resources\r\n"
+                                                    "      << /ExtGState\r\n"
+                                                    "            << /a0 << /ca 1 /CA 1 >>"
+                                                    "      >>\r\n"
+                                                    "         /Pattern\r\n"
+                                                    "            << /res%d %d 0 R >>\r\n"
+                                                    "      >>\r\n"
+                                                    "   /Group\r\n"
+                                                    "      << /Type /Group\r\n"
+                                                    "         /S /Transparency\r\n"
+                                                    "         /CS /DeviceGray\r\n"
+                                                    "      >>\r\n",
+                                                    surface->width,
+                                                    surface->height,
+                                                    gradient_mask.id,
+                                                    gradient_mask.id);
+
+    _cairo_output_stream_printf (surface->output,
+                                 "q\r\n"
+                                 "/a0 gs\r\n"
+                                 "/Pattern cs /res%d scn\r\n"
+                                 "0 0 %f %f re\r\n"
+                                 "f\r\n"
+                                 "Q\r\n",
+                                 gradient_mask.id,
+                                 surface->width,
+                                 surface->height);
+
+    _cairo_pdf_surface_close_stream (surface);
+
+    smask_resource = _cairo_pdf_surface_new_object (surface);
+    _cairo_output_stream_printf (surface->output,
+                                 "%d 0 obj\r\n"
+                                 "<< /Type /Mask\r\n"
+                                 "   /S /Luminosity\r\n"
+                                 "   /G %d 0 R\r\n"
+                                 "   /BC [ 0.0 ]\r\n"
+                                 ">>\r\n"
+                                 "endobj\r\n",
+                                 smask_resource.id,
+                                 xobj_resource.id);
+
+    /* Create GState which uses the transparency group as an SMask. */
+    gstate_resource = _cairo_pdf_surface_new_object (surface);
+    _cairo_output_stream_printf (surface->output,
+                                 "%d 0 obj\r\n"
+                                 "<< /Type /ExtGState\r\n"
+                                 "   /SMask %d 0 R\r\n"
+                                 "   /ca 1\r\n"
+                                 "   /CA 1\r\n"
+                                 "   /AIS false\r\n"
+                                 ">>\r\n"
+                                 "endobj\r\n",
+                                 gstate_resource.id,
+                                 smask_resource.id);
+
+    _cairo_pdf_surface_add_smask (surface, gstate_resource);
+
+    return gstate_resource;
 }
 
 static cairo_status_t
-_cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_linear_pattern_t *pattern)
+_cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t    *surface,
+                                        cairo_linear_pattern_t *pattern)
 {
-    cairo_pdf_resource_t function, pattern_resource, alpha;
+    cairo_pdf_resource_t pattern_resource, smask;
+    cairo_pdf_resource_t color_function, alpha_function;
     double x0, y0, x1, y1;
     cairo_matrix_t p2u;
+    cairo_extend_t extend;
     cairo_status_t status;
 
+    extend = cairo_pattern_get_extend (&pattern->base.base);
     status = _cairo_pdf_surface_pause_content_stream (surface);
     if (status)
 	return status;
 
-    function = _cairo_pdf_surface_emit_pattern_stops (surface, &pattern->base);
-    if (function.id == 0)
-	return CAIRO_STATUS_NO_MEMORY;
+    status = _cairo_pdf_surface_emit_pattern_stops (surface,
+                                                    &pattern->base,
+                                                    &color_function,
+                                                    &alpha_function);
+    if (status)
+	return status;
 
     p2u = pattern->base.base.matrix;
     status = cairo_matrix_invert (&p2u);
@@ -1202,61 +1453,110 @@ _cairo_pdf_surface_emit_linear_pattern (
 
     pattern_resource = _cairo_pdf_surface_new_object (surface);
     _cairo_output_stream_printf (surface->output,
-				 "%d 0 obj\r\n"
-				 "<< /Type /Pattern\r\n"
-				 "   /PatternType 2\r\n"
-				 "   /Matrix [ 1 0 0 -1 0 %f ]\r\n"
-				 "   /Shading\r\n"
-				 "      << /ShadingType 2\r\n"
-				 "         /ColorSpace /DeviceRGB\r\n"
-				 "         /Coords [ %f %f %f %f ]\r\n"
-				 "         /Function %d 0 R\r\n"
-				 "         /Extend [ true true ]\r\n"
-				 "      >>\r\n"
-				 ">>\r\n"
-				 "endobj\r\n",
-				 pattern_resource.id,
-				 surface->height,
-				 x0, y0, x1, y1,
-				 function.id);
+                                 "%d 0 obj\r\n"
+                                 "<< /Type /Pattern\r\n"
+                                 "   /PatternType 2\r\n"
+                                 "   /Matrix [ 1 0 0 -1 0 %f ]\r\n"
+                                 "   /Shading\r\n"
+                                 "      << /ShadingType 2\r\n"
+                                 "         /ColorSpace /DeviceRGB\r\n"
+                                 "         /Coords [ %f %f %f %f ]\r\n"
+                                 "         /Function %d 0 R\r\n",
+                                 pattern_resource.id,
+                                 surface->height,
+                                 x0, y0, x1, y1,
+                                 color_function.id);
 
-    status = _cairo_pdf_surface_add_pattern (surface, pattern_resource);
-    if (status)
-	return status;
+    if (extend == CAIRO_EXTEND_PAD) {
+        _cairo_output_stream_printf (surface->output,
+                                     "         /Extend [ true true ]\r\n");
+    } else {
+        _cairo_output_stream_printf (surface->output,
+                                     "         /Extend [ false false ]\r\n");
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                 "      >>\r\n"
+                                 ">>\r\n"
+                                 "endobj\r\n");
+
+    if (alpha_function.id == 0) {
+         surface->emitted_pattern.smask.id = 0;
+    } else {
+        cairo_pdf_resource_t mask_resource;
+
+        /* Create pattern for SMask. */
+        mask_resource = _cairo_pdf_surface_new_object (surface);
+        _cairo_output_stream_printf (surface->output,
+                                     "%d 0 obj\r\n"
+                                     "<< /Type /Pattern\r\n"
+                                     "   /PatternType 2\r\n"
+                                     "   /Matrix [ 1 0 0 -1 0 %f ]\r\n"
+                                     "   /Shading\r\n"
+                                     "      << /ShadingType 2\r\n"
+                                     "         /ColorSpace /DeviceGray\r\n"
+                                     "         /Coords [ %f %f %f %f ]\r\n"
+                                     "         /Function %d 0 R\r\n",
+                                     mask_resource.id,
+                                     surface->height,
+                                     x0, y0, x1, y1,
+                                     alpha_function.id);
+
+        if (extend == CAIRO_EXTEND_PAD) {
+            _cairo_output_stream_printf (surface->output,
+                                         "         /Extend [ true true ]\r\n");
+        } else {
+            _cairo_output_stream_printf (surface->output,
+                                         "         /Extend [ false false ]\r\n");
+        }
 
-    status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+        _cairo_output_stream_printf (surface->output,
+                                     "      >>\r\n"
+                                     ">>\r\n"
+                                     "endobj\r\n");
+        status = _cairo_pdf_surface_add_pattern (surface, mask_resource);
+        if (status)
+            return status;
+
+        smask = cairo_pdf_surface_emit_transparency_group (surface, mask_resource);
+        surface->emitted_pattern.smask = smask;
+    }
+
+    surface->emitted_pattern.type = CAIRO_PATTERN_TYPE_LINEAR;
+    status = _cairo_pdf_surface_add_alpha (surface, 1, &surface->emitted_pattern.alpha);
     if (status)
-	return status;
+        return status;
 
-    /* Use pattern */
-    /* With some work, we could separate the stroking
-     * or non-stroking pattern here as actually needed. */
-    _cairo_output_stream_printf (surface->output,
-				 "/Pattern CS /res%d SCN "
-				 "/Pattern cs /res%d scn "
-				 "/a%d gs\r\n",
-				 pattern_resource.id,
-				 pattern_resource.id,
-				 alpha.id);
+    surface->emitted_pattern.pattern = pattern_resource;
+    status = _cairo_pdf_surface_add_pattern (surface, pattern_resource);
+    if (status)
+        return status;
 
     return _cairo_pdf_surface_resume_content_stream (surface);
 }
 
 static cairo_status_t
-_cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_radial_pattern_t *pattern)
+_cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t    *surface,
+                                        cairo_radial_pattern_t *pattern)
 {
-    cairo_pdf_resource_t function, pattern_resource, alpha;
+    cairo_pdf_resource_t pattern_resource, smask;
+    cairo_pdf_resource_t color_function, alpha_function;
     double x0, y0, x1, y1, r0, r1;
-    cairo_matrix_t p2u;
+    cairo_matrix_t p2u, pdf_p2d;
+    cairo_extend_t extend;
     cairo_status_t status;
 
+    extend = cairo_pattern_get_extend (&pattern->base.base);
     status = _cairo_pdf_surface_pause_content_stream (surface);
     if (status)
 	return status;
 
-    function = _cairo_pdf_surface_emit_pattern_stops (surface, &pattern->base);
-    if (function.id == 0)
-	return CAIRO_STATUS_NO_MEMORY;
+    status = _cairo_pdf_surface_emit_pattern_stops (surface,
+                                                    &pattern->base,
+                                                    &color_function,
+                                                    &alpha_function);
+    if (status)
+	return status;
 
     p2u = pattern->base.base.matrix;
     status = cairo_matrix_invert (&p2u);
@@ -1266,62 +1566,99 @@ _cairo_pdf_surface_emit_radial_pattern (
     x0 = _cairo_fixed_to_double (pattern->gradient.c1.x);
     y0 = _cairo_fixed_to_double (pattern->gradient.c1.y);
     r0 = _cairo_fixed_to_double (pattern->gradient.c1.radius);
-    cairo_matrix_transform_point (&p2u, &x0, &y0);
     x1 = _cairo_fixed_to_double (pattern->gradient.c2.x);
     y1 = _cairo_fixed_to_double (pattern->gradient.c2.y);
     r1 = _cairo_fixed_to_double (pattern->gradient.c2.radius);
-    cairo_matrix_transform_point (&p2u, &x1, &y1);
 
-    /* FIXME: This is surely crack, but how should you scale a radius
-     * in a non-orthogonal coordinate system? */
-    cairo_matrix_transform_distance (&p2u, &r0, &r1);
-
-    /* FIXME: There is a difference between the cairo gradient extend
-     * semantics and PDF extend semantics. PDFs extend=false means
-     * that nothing is painted outside the gradient boundaries,
-     * whereas cairo takes this to mean that the end color is padded
-     * to infinity. Setting extend=true in PDF gives the cairo default
-     * behavoir, not yet sure how to implement the cairo mirror and
-     * repeat behaviour. */
+    cairo_matrix_init_identity (&pdf_p2d);
+    cairo_matrix_translate (&pdf_p2d, 0.0, surface->height);
+    cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
+    cairo_matrix_multiply (&pdf_p2d, &p2u, &pdf_p2d);
+
     pattern_resource = _cairo_pdf_surface_new_object (surface);
     _cairo_output_stream_printf (surface->output,
-				 "%d 0 obj\r\n"
-				 "<< /Type /Pattern\r\n"
-				 "   /PatternType 2\r\n"
-				 "   /Matrix [ 1 0 0 -1 0 %f ]\r\n"
-				 "   /Shading\r\n"
-				 "      << /ShadingType 3\r\n"
-				 "         /ColorSpace /DeviceRGB\r\n"
-				 "         /Coords [ %f %f %f %f %f %f ]\r\n"
-				 "         /Function %d 0 R\r\n"
-				 "         /Extend [ true true ]\r\n"
-				 "      >>\r\n"
-				 ">>\r\n"
-				 "endobj\r\n",
-				 pattern_resource.id,
-				 surface->height,
-				 x0, y0, r0, x1, y1, r1,
-				 function.id);
+                                 "%d 0 obj\r\n"
+                                 "<< /Type /Pattern\r\n"
+                                 "   /PatternType 2\r\n"
+                                 "   /Matrix [ %f %f %f %f %f %f ]\r\n"
+                                 "   /Shading\r\n"
+                                 "      << /ShadingType 3\r\n"
+                                 "         /ColorSpace /DeviceRGB\r\n"
+                                 "         /Coords [ %f %f %f %f %f %f ]\r\n"
+                                 "         /Function %d 0 R\r\n",
+                                 pattern_resource.id,
+                                 pdf_p2d.xx, pdf_p2d.yx,
+                                 pdf_p2d.xy, pdf_p2d.yy,
+                                 pdf_p2d.x0, pdf_p2d.y0,
+                                 x0, y0, r0, x1, y1, r1,
+                                 color_function.id);
 
-    status = _cairo_pdf_surface_add_pattern (surface, pattern_resource);
+    if (extend == CAIRO_EXTEND_PAD) {
+        _cairo_output_stream_printf (surface->output,
+                                     "         /Extend [ true true ]\r\n");
+    } else {
+        _cairo_output_stream_printf (surface->output,
+                                     "         /Extend [ false false ]\r\n");
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                 "      >>\r\n"
+                                 ">>\r\n"
+                                 "endobj\r\n");
+
+    if (alpha_function.id == 0) {
+        surface->emitted_pattern.smask.id = 0;
+    } else {
+        cairo_pdf_resource_t mask_resource;
+
+        /* Create pattern for SMask. */
+        mask_resource = _cairo_pdf_surface_new_object (surface);
+        _cairo_output_stream_printf (surface->output,
+                                     "%d 0 obj\r\n"
+                                     "<< /Type /Pattern\r\n"
+                                     "   /PatternType 2\r\n"
+                                     "   /Matrix [ %f %f %f %f %f %f ]\r\n"
+                                     "   /Shading\r\n"
+                                     "      << /ShadingType 3\r\n"
+                                     "         /ColorSpace /DeviceGray\r\n"
+                                     "         /Coords [ %f %f %f %f %f %f ]\r\n"
+                                     "         /Function %d 0 R\r\n",
+                                     mask_resource.id,
+                                     pdf_p2d.xx, pdf_p2d.yx,
+                                     pdf_p2d.xy, pdf_p2d.yy,
+                                     pdf_p2d.x0, pdf_p2d.y0,
+                                     x0, y0, r0, x1, y1, r1,
+                                     alpha_function.id);
+
+        if (extend == CAIRO_EXTEND_PAD) {
+            _cairo_output_stream_printf (surface->output,
+                                         "         /Extend [ true true ]\r\n");
+        } else {
+            _cairo_output_stream_printf (surface->output,
+                                         "         /Extend [ false false ]\r\n");
+        }
+
+        _cairo_output_stream_printf (surface->output,
+                                     "      >>\r\n"
+                                     ">>\r\n"
+                                     "endobj\r\n");
+
+        smask = cairo_pdf_surface_emit_transparency_group (surface, mask_resource);
+        surface->emitted_pattern.smask = smask;
+    }
+
+    surface->emitted_pattern.type = CAIRO_PATTERN_TYPE_RADIAL;
+
+    status = _cairo_pdf_surface_add_alpha (surface, 1.0, &surface->emitted_pattern.alpha);
     if (status)
 	return status;
 
-    status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+    surface->emitted_pattern.pattern = pattern_resource;
+
+    status = _cairo_pdf_surface_add_pattern (surface, pattern_resource);
     if (status)
 	return status;
 
-    /* Use pattern */
-    /* With some work, we could separate the stroking
-     * or non-stroking pattern here as actually needed. */
-    _cairo_output_stream_printf (surface->output,
-				 "/Pattern CS /res%d SCN "
-				 "/Pattern cs /res%d scn "
-				 "/a%d gs\r\n",
-				 pattern_resource.id,
-				 pattern_resource.id,
-				 alpha.id);
-
     return _cairo_pdf_surface_resume_content_stream (surface);
 }
 
@@ -1340,13 +1677,52 @@ _cairo_pdf_surface_emit_pattern (cairo_p
 
     case CAIRO_PATTERN_TYPE_RADIAL:
 	return _cairo_pdf_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern);
-
     }
 
     ASSERT_NOT_REACHED;
     return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
 }
 
+static cairo_status_t
+_cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
+                                   cairo_bool_t         is_stroke)
+{
+    if (surface->emitted_pattern.type == CAIRO_PATTERN_TYPE_SOLID) {
+	_cairo_output_stream_printf (surface->output,
+                                     "%f %f %f ",
+                                     surface->emitted_pattern.red,
+                                     surface->emitted_pattern.green,
+                                     surface->emitted_pattern.blue);
+
+        if (is_stroke)
+            _cairo_output_stream_printf (surface->output, "RG ");
+        else
+            _cairo_output_stream_printf (surface->output, "rg ");
+
+        _cairo_output_stream_printf (surface->output,
+                                     "/a%d gs\r\n",
+                                     surface->emitted_pattern.alpha);
+    } else {
+        if (is_stroke) {
+            _cairo_output_stream_printf (surface->output,
+                                         "/Pattern CS /res%d SCN ",
+                                         surface->emitted_pattern.pattern);
+        } else {
+            _cairo_output_stream_printf (surface->output,
+                                         "/Pattern cs /res%d scn ",
+                                         surface->emitted_pattern.pattern);
+        }
+
+        _cairo_output_stream_printf (surface->output,
+                                     "/a%d gs ",
+                                     surface->emitted_pattern.alpha );
+
+        _cairo_output_stream_printf (surface->output, "\r\n");
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static cairo_int_status_t
 _cairo_pdf_surface_copy_page (void *abstract_surface)
 {
@@ -1548,10 +1924,10 @@ _cairo_pdf_surface_write_info (cairo_pdf
 static void
 _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface)
 {
-    cairo_pdf_resource_t page, *res;
+    cairo_pdf_resource_t page, *res, smask;
     cairo_pdf_font_t font;
     int num_pages, num_fonts, i;
-    int num_alphas, num_resources;
+    int num_alphas, num_smasks, num_resources;
     double alpha;
 
     _cairo_pdf_surface_update_object (surface, surface->pages_resource);
@@ -1573,19 +1949,25 @@ _cairo_pdf_surface_write_pages (cairo_pd
     _cairo_output_stream_printf (surface->output, "   /Resources <<\r\n");
 
     num_alphas =  _cairo_array_num_elements (&surface->alphas);
-    if (num_alphas > 0) {
+    num_smasks =  _cairo_array_num_elements (&surface->smasks);
+    if (num_alphas > 0 || num_smasks > 0) {
 	_cairo_output_stream_printf (surface->output,
 				     "      /ExtGState <<\r\n");
 
 	for (i = 0; i < num_alphas; i++) {
-	    /* With some work, we could separate the stroking
-	     * or non-stroking alpha here as actually needed. */
 	    _cairo_array_copy_element (&surface->alphas, i, &alpha);
 	    _cairo_output_stream_printf (surface->output,
 					 "         /a%d << /CA %f /ca %f >>\r\n",
 					 i, alpha, alpha);
 	}
 
+        for (i = 0; i < num_smasks; i++) {
+	    _cairo_array_copy_element (&surface->smasks, i, &smask);
+	    _cairo_output_stream_printf (surface->output,
+					 "         /sm%d %d 0 R\r\n",
+					 smask.id, smask.id);
+	}
+
 	_cairo_output_stream_printf (surface->output,
 				     "      >>\r\n");
     }
@@ -1621,14 +2003,20 @@ _cairo_pdf_surface_write_pages (cairo_pd
 				     " >>\r\n");
     }
 
-    _cairo_output_stream_printf (surface->output,"      /Font <<\r\n");
     num_fonts = _cairo_array_num_elements (&surface->fonts);
-    for (i = 0; i < num_fonts; i++) {
-	_cairo_array_copy_element (&surface->fonts, i, &font);
-	_cairo_output_stream_printf (surface->output, "         /CairoFont-%d-%d %d 0 R\r\n",
-				     font.font_id, font.subset_id, font.subset_resource.id);
+    if (num_fonts > 0) {
+        _cairo_output_stream_printf (surface->output,"      /Font <<\r\n");
+        num_fonts = _cairo_array_num_elements (&surface->fonts);
+        for (i = 0; i < num_fonts; i++) {
+            _cairo_array_copy_element (&surface->fonts, i, &font);
+            _cairo_output_stream_printf (surface->output,
+                                         "         /CairoFont-%d-%d %d 0 R\r\n",
+                                         font.font_id,
+                                         font.subset_id,
+                                         font.subset_resource.id);
+        }
+        _cairo_output_stream_printf (surface->output, "      >>\r\n");
     }
-    _cairo_output_stream_printf (surface->output, "      >>\r\n");
 
     _cairo_output_stream_printf (surface->output,
 				 "   >>\r\n");
@@ -2646,11 +3034,49 @@ _surface_pattern_supported (cairo_surfac
 }
 
 static cairo_bool_t
+_gradient_pattern_supported (cairo_pattern_t *pattern)
+{
+    cairo_extend_t extend;
+
+    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->gradient.c1.x);
+        y1 = _cairo_fixed_to_double (radial->gradient.c1.y);
+        r1 = _cairo_fixed_to_double (radial->gradient.c1.radius);
+        x2 = _cairo_fixed_to_double (radial->gradient.c2.x);
+        y2 = _cairo_fixed_to_double (radial->gradient.c2.y);
+        r2 = _cairo_fixed_to_double (radial->gradient.c2.radius);
+
+        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_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 (pattern);
+
     if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
 	return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
 
@@ -2693,6 +3119,7 @@ _cairo_pdf_surface_paint (void			*abstra
 			  cairo_pattern_t	*source)
 {
     cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_pdf_resource_t group = {0}; /* squelch bogus compiler warning */
     cairo_status_t status;
 
     if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
@@ -2712,10 +3139,35 @@ _cairo_pdf_surface_paint (void			*abstra
     if (status)
 	return status;
 
+    if (surface->emitted_pattern.smask.id != 0) {
+	status = _cairo_pdf_surface_begin_group (surface, &group);
+        if (status)
+            return status;
+        _cairo_output_stream_printf (surface->output,
+                                     "1 0 0 -1 0 %f cm\r\n",
+                                     surface->height);
+    } else {
+        _cairo_output_stream_printf (surface->output, "q ");
+    }
+
+    _cairo_pdf_surface_select_pattern (surface, FALSE);
+
     _cairo_output_stream_printf (surface->output,
 				 "0 0 %f %f re f\r\n",
 				 surface->width, surface->height);
 
+    if (surface->emitted_pattern.smask.id != 0) {
+        _cairo_pdf_surface_end_group (surface);
+
+        _cairo_output_stream_printf (surface->output,
+                                     "q 1 0 0 -1 0 %f cm /sm%d gs /res%d Do Q\r\n",
+                                     surface->height,
+                                     surface->emitted_pattern.smask,
+                                     group.id);
+    } else {
+        _cairo_output_stream_printf (surface->output, "Q\r\n");
+    }
+
     return _cairo_output_stream_get_status (surface->output);
 }
 
@@ -2813,6 +3265,7 @@ _cairo_pdf_surface_stroke (void			*abstr
 			   cairo_antialias_t	 antialias)
 {
     cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_pdf_resource_t group = {0}; /* squelch bogus compiler warning */
     pdf_path_info_t info;
     cairo_status_t status;
 
@@ -2825,6 +3278,19 @@ _cairo_pdf_surface_stroke (void			*abstr
     if (status)
 	return status;
 
+    if (surface->emitted_pattern.smask.id != 0) {
+	status = _cairo_pdf_surface_begin_group (surface, &group);
+        if (status)
+            return status;
+        _cairo_output_stream_printf (surface->output,
+                                     "1 0 0 -1 0 %f cm\r\n",
+                                     surface->height);
+    } else {
+        _cairo_output_stream_printf (surface->output, "q ");
+    }
+
+    _cairo_pdf_surface_select_pattern (surface, TRUE);
+
     status = _cairo_pdf_surface_emit_stroke_style (surface,
 						   style);
     if (status)
@@ -2848,6 +3314,18 @@ _cairo_pdf_surface_stroke (void			*abstr
 
     _cairo_output_stream_printf (surface->output, "S Q\r\n");
 
+    if (surface->emitted_pattern.smask.id != 0) {
+        _cairo_pdf_surface_end_group (surface);
+
+        _cairo_output_stream_printf (surface->output,
+                                     "q 1 0 0 -1 0 %f cm /sm%d gs /res%d Do Q\r\n",
+                                     surface->height,
+                                     surface->emitted_pattern.smask,
+                                     group.id);
+    } else {
+        _cairo_output_stream_printf (surface->output, "Q\r\n");
+    }
+
     return status;
 }
 
@@ -2861,6 +3339,7 @@ _cairo_pdf_surface_fill (void			*abstrac
 			 cairo_antialias_t	 antialias)
 {
     cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_pdf_resource_t group = {0}; /* squelch bogus compiler warning */
     const char *pdf_operator;
     cairo_status_t status;
     pdf_path_info_t info;
@@ -2874,9 +3353,20 @@ _cairo_pdf_surface_fill (void			*abstrac
     if (status)
 	return status;
 
+    if (surface->emitted_pattern.smask.id != 0) {
+	status = _cairo_pdf_surface_begin_group (surface, &group);
+        if (status)
+            return status;
+        _cairo_output_stream_printf (surface->output,
+                                     "1 0 0 -1 0 %f cm\r\n",
+                                     surface->height);
+    } else {
+        _cairo_output_stream_printf (surface->output, "q ");
+    }
+
+    _cairo_pdf_surface_select_pattern (surface, FALSE);
     info.output = surface->output;
     info.ctm_inverse = NULL;
-
     status = _cairo_path_fixed_interpret (path,
 					  CAIRO_DIRECTION_FORWARD,
 					  _cairo_pdf_path_move_to,
@@ -2900,6 +3390,18 @@ _cairo_pdf_surface_fill (void			*abstrac
 				 "%s\r\n",
 				 pdf_operator);
 
+    if (surface->emitted_pattern.smask.id != 0) {
+        _cairo_pdf_surface_end_group (surface);
+
+        _cairo_output_stream_printf (surface->output,
+                                     "q 1 0 0 -1 0 %f cm /sm%d gs /res%d Do Q\r\n",
+                                     surface->height,
+                                     surface->emitted_pattern.smask,
+                                     group.id);
+    } else {
+        _cairo_output_stream_printf (surface->output, "Q\r\n");
+    }
+
     return status;
 }
 
@@ -2914,6 +3416,7 @@ _cairo_pdf_surface_show_glyphs (void			*
 				cairo_scaled_font_t	*scaled_font)
 {
     cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_pdf_resource_t group = {0}; /* squelch bogus compiler warning */
     unsigned int current_subset_id = (unsigned int)-1;
     cairo_scaled_font_subsets_glyph_t subset_glyph;
     cairo_bool_t diagonal, in_TJ;
@@ -2931,6 +3434,19 @@ _cairo_pdf_surface_show_glyphs (void			*
     if (status)
 	return status;
 
+    if (surface->emitted_pattern.smask.id != 0) {
+	status = _cairo_pdf_surface_begin_group (surface, &group);
+        if (status)
+            return status;
+        _cairo_output_stream_printf (surface->output,
+                                     "1 0 0 -1 0 %f cm\r\n",
+                                     surface->height);
+    } else {
+        _cairo_output_stream_printf (surface->output, "q ");
+    }
+
+    _cairo_pdf_surface_select_pattern (surface, FALSE);
+
     _cairo_output_stream_printf (surface->output,
 				 "BT\r\n");
 
@@ -3068,6 +3584,18 @@ _cairo_pdf_surface_show_glyphs (void			*
     _cairo_output_stream_printf (surface->output,
 				 "ET\r\n");
 
+    if (surface->emitted_pattern.smask.id != 0) {
+        _cairo_pdf_surface_end_group (surface);
+
+        _cairo_output_stream_printf (surface->output,
+                                     "q 1 0 0 -1 0 %f cm /sm%d gs /res%d Do Q\r\n",
+                                     surface->height,
+                                     surface->emitted_pattern.smask,
+                                     group.id);
+    } else {
+        _cairo_output_stream_printf (surface->output, "Q\r\n");
+    }
+
     return _cairo_output_stream_get_status (surface->output);
 }
 


More information about the cairo-commit mailing list