[cairo-commit] 9 commits - src/cairo-pdf-operators.c src/cairo-pdf-operators-private.h src/cairo-pdf-surface.c src/cairo-pdf-surface-private.h src/cairo-png.c src/cairo-ps-surface.c src/cairo-scaled-font-subsets.c src/cairo-scaled-font-subsets-private.h test/ft-show-glyphs-positioning-pdf-ref.png test/text-rotate-pdf-ref.png test/text-rotate-ps-ref.png

Adrian Johnson ajohnson at kemper.freedesktop.org
Wed Jun 4 06:58:39 PDT 2008


 src/cairo-pdf-operators-private.h           |   30 +
 src/cairo-pdf-operators.c                   |  579 +++++++++++++++++++---------
 src/cairo-pdf-surface-private.h             |    7 
 src/cairo-pdf-surface.c                     |  165 ++++++-
 src/cairo-png.c                             |    3 
 src/cairo-ps-surface.c                      |   51 ++
 src/cairo-scaled-font-subsets-private.h     |    1 
 src/cairo-scaled-font-subsets.c             |   10 
 test/ft-show-glyphs-positioning-pdf-ref.png |binary
 test/text-rotate-pdf-ref.png                |binary
 test/text-rotate-ps-ref.png                 |binary
 11 files changed, 629 insertions(+), 217 deletions(-)

New commits:
commit 4c5370dad87cb822a17905dd8dbb058fce11d132
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Jun 4 23:27:05 2008 +0930

    Update PDF/PS ref images of text-rotate and ft-show-glyphs-positioning

diff --git a/test/ft-show-glyphs-positioning-pdf-ref.png b/test/ft-show-glyphs-positioning-pdf-ref.png
index 11f6953..796d4eb 100644
Binary files a/test/ft-show-glyphs-positioning-pdf-ref.png and b/test/ft-show-glyphs-positioning-pdf-ref.png differ
diff --git a/test/text-rotate-pdf-ref.png b/test/text-rotate-pdf-ref.png
new file mode 100644
index 0000000..bdd64e6
Binary files /dev/null and b/test/text-rotate-pdf-ref.png differ
diff --git a/test/text-rotate-ps-ref.png b/test/text-rotate-ps-ref.png
index a20e7f0..125736b 100644
Binary files a/test/text-rotate-ps-ref.png and b/test/text-rotate-ps-ref.png differ
commit a922a0c5cf2c7862eba4db0b71bda75136a9012a
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Jun 4 22:59:53 2008 +0930

    Bit swap when writing FORMAT_A1 to PNG on little endian

diff --git a/src/cairo-png.c b/src/cairo-png.c
index 59e0419..ae3a1f7 100644
--- a/src/cairo-png.c
+++ b/src/cairo-png.c
@@ -197,6 +197,9 @@ write_png (cairo_surface_t	*surface,
     case CAIRO_FORMAT_A1:
 	depth = 1;
 	png_color_type = PNG_COLOR_TYPE_GRAY;
+#ifndef WORDS_BIGENDIAN
+	png_set_packswap (png);
+#endif
 	break;
     default:
 	status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
commit 1d8596d5b068c383c236f312f7dc753911a5f9b7
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Jun 4 22:59:33 2008 +0930

    Optimize PDF operators show_glyphs() to use Tj operator
    
    The Tj operator displays a string of glyphs positioned at their glyph
    advances.

diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c
index 0aef2f3..463af87 100644
--- a/src/cairo-pdf-operators.c
+++ b/src/cairo-pdf-operators.c
@@ -806,6 +806,27 @@ _cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t 	*pdf_operators,
 
 #define GLYPH_POSITION_TOLERANCE 0.001
 
+/* Emit the string of glyphs using the 'Tj' operator. This requires
+ * that the glyphs are positioned at their natural glyph advances. */
+static cairo_status_t
+_cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t   *pdf_operators,
+					cairo_output_stream_t  	*stream)
+{
+    int i;
+
+    _cairo_output_stream_printf (stream, "<");
+    for (i = 0; i < pdf_operators->num_glyphs; i++) {
+	_cairo_output_stream_printf (stream,
+				     "%0*x",
+				     pdf_operators->hex_width,
+				     pdf_operators->glyphs[i].glyph_index);
+	pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
+    }
+    _cairo_output_stream_printf (stream, ">Tj\n");
+
+    return _cairo_output_stream_get_status (stream);
+}
+
 /* Emit the string of glyphs using the 'TJ' operator.
  *
  * The TJ operator takes an array of strings of glyphs. Each string of
@@ -867,6 +888,8 @@ _cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t    *pdf_operators)
 {
     cairo_output_stream_t *word_wrap_stream;
     cairo_status_t status;
+    int i;
+    double x;
 
     if (pdf_operators->num_glyphs == 0)
 	return 0;
@@ -876,8 +899,20 @@ _cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t    *pdf_operators)
     if (status)
 	return _cairo_output_stream_destroy (word_wrap_stream);
 
-    status = _cairo_pdf_operators_emit_glyph_string_with_positioning (
-	pdf_operators, word_wrap_stream);
+    /* Check if glyph advance used to position every glyph */
+    x = pdf_operators->glyphs[0].x_position;
+    for (i = 0; i < pdf_operators->num_glyphs; i++) {
+	if (fabs(pdf_operators->glyphs[i].x_position - x) > GLYPH_POSITION_TOLERANCE)
+	    break;
+	x += pdf_operators->glyphs[i].x_advance;
+    }
+    if (i == pdf_operators->num_glyphs) {
+	status = _cairo_pdf_operators_emit_glyph_string (pdf_operators,
+							 word_wrap_stream);
+    } else {
+	status = _cairo_pdf_operators_emit_glyph_string_with_positioning (
+	    pdf_operators, word_wrap_stream);
+    }
 
     pdf_operators->num_glyphs = 0;
     status = _cairo_output_stream_destroy (word_wrap_stream);
commit 71fe8c8881d495e8c1bd2ebbb8748761adc6bcd9
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Jun 4 22:57:43 2008 +0930

    PS: Make PDF emulation work with the new show_glyphs optimizations
    
    The implementation of the Tf, Td, and Tm operators has been extended
    to allow calling these operators in any order.

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index b4f0718..0a08667 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -172,7 +172,6 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
 				 "/n { newpath } bind def\n"
 				 "/W { clip } bind def\n"
 				 "/W* { eoclip } bind def\n"
-				 "/Tf { pop /cairo_font exch def } bind def\n"
 				 "/BT { } bind def\n"
 				 "/ET { } bind def\n"
 				 "/Tj { show } bind def\n"
@@ -183,11 +182,15 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
 				 "    { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse\n"
 				 "  } forall\n"
 				 "} bind def\n"
-				 "/Td { matrix translate cairo_font_matrix matrix concatmatrix aload\n"
-				 "      /cairo_font_matrix exch def 6 2 roll 0 0 6 array astore\n"
-				 "      cairo_font exch selectfont moveto } bind def\n"
-				 "/Tm { 6 copy 6 array astore /cairo_font_matrix exch def 6 2 roll 0 0\n"
-				 "      6 array astore cairo_font exch selectfont moveto } bind def\n"
+				 "/cairo_selectfont { cairo_font_matrix aload pop 6 2 roll\n"
+				 "      0 0 6 array astore cairo_font exch selectfont moveto } bind def\n"
+				 "/Tf { pop /cairo_font exch def /cairo_font_matrix where\n"
+				 "      { cairo_selectfont } if } bind def\n"
+				 "/Td { matrix translate cairo_font_matrix matrix concatmatrix\n"
+				 "      /cairo_font_matrix exch def /cairo_font where\n"
+				 "      { cairo_selectfont } if } bind def\n"
+				 "/Tm { 6 array astore /cairo_font_matrix exch def /cairo_font where\n"
+				 "      { cairo_selectfont } if } bind def\n"
 				 "/g { setgray } bind def\n"
 				 "/rg { setrgbcolor } bind def\n");
 
commit 83e4825fae85acb49ec032c0ddf51a615ee76a9e
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Jun 4 22:47:50 2008 +0930

    Rewrite _cairo_pdf_operators_show_glyphs()
    
    Rewrite the PDF operators show_glyphs() function to make it more
    maintainable and better optimized.
    
    The changes include:
    
    - Use a separate function to output each text operator and update the
    internal state.
    
    - Store glyphs in a buffer until they can be written out as one
    string. This reduces the complexity of the code for emitting glyph
    strings and significantly optimizes the output size as glyphs from
    multiple calls to show_glyphs() can be accumulated and written in one
    string.
    
    - The code now better handles rotated text. Previously, using rotated
    text resulted in the text matrix emitted for every glyph. Now rotated
    text can be emitted as strings in the some way as non rotated
    text. This is particulary useful for printing in landscape mode where
    all text on the page is rotated.

diff --git a/src/cairo-pdf-operators-private.h b/src/cairo-pdf-operators-private.h
index 3486d13..e29834b 100644
--- a/src/cairo-pdf-operators-private.h
+++ b/src/cairo-pdf-operators-private.h
@@ -45,17 +45,41 @@
 #include "cairo-compiler-private.h"
 #include "cairo-types-private.h"
 
+/* The glyph buffer size is based on the expected maximum glyphs in a
+ * line so that an entire line can be emitted in as one string. If the
+ * glyphs in a line exceeds this size the only downside is the slight
+ * overhead of emitting two strings.
+ */
+#define PDF_GLYPH_BUFFER_SIZE 200
+
 typedef cairo_status_t (*cairo_pdf_operators_use_font_subset_t) (unsigned int  font_id,
 								 unsigned int  subset_id,
 								 void         *closure);
 
+typedef struct _cairo_pdf_glyph {
+    unsigned int glyph_index;
+    double x_position;
+    double x_advance;
+} cairo_pdf_glyph_t;
+
 typedef struct _cairo_pdf_operators {
     cairo_output_stream_t *stream;
     cairo_matrix_t cairo_to_pdf;
     cairo_scaled_font_subsets_t *font_subsets;
     cairo_pdf_operators_use_font_subset_t use_font_subset;
     void *use_font_subset_closure;
-    cairo_bool_t in_text;
+    cairo_bool_t in_text_object; /* inside BT/ET pair */
+
+    /* PDF text state */
+    unsigned int font_id;
+    unsigned int subset_id;
+    cairo_matrix_t text_matrix; /* PDF text matrix (Tlm in the PDF reference) */
+    cairo_matrix_t cairo_to_pdftext; /* translate cairo coords to PDF text space */
+    double cur_x; /* Current position in PDF text space (Tm in the PDF reference) */
+    double cur_y;
+    int hex_width;
+    int num_glyphs;
+    cairo_pdf_glyph_t glyphs[PDF_GLYPH_BUFFER_SIZE];
 } cairo_pdf_operators_t;
 
 cairo_private void
@@ -81,7 +105,7 @@ cairo_private void
 _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators,
 					      cairo_matrix_t 	    *cairo_to_pdf);
 
-cairo_private cairo_int_status_t
+cairo_private cairo_status_t
 _cairo_pdf_operators_flush (cairo_pdf_operators_t	 *pdf_operators);
 
 cairo_private cairo_int_status_t
diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c
index d4b9a3c..0aef2f3 100644
--- a/src/cairo-pdf-operators.c
+++ b/src/cairo-pdf-operators.c
@@ -47,7 +47,7 @@
 
 #include <ctype.h>
 
-static cairo_int_status_t
+static cairo_status_t
 _cairo_pdf_operators_end_text (cairo_pdf_operators_t    *pdf_operators);
 
 
@@ -62,7 +62,8 @@ _cairo_pdf_operators_init (cairo_pdf_operators_t	*pdf_operators,
     pdf_operators->font_subsets = font_subsets;
     pdf_operators->use_font_subset = NULL;
     pdf_operators->use_font_subset_closure = NULL;
-    pdf_operators->in_text = FALSE;
+    pdf_operators->in_text_object = FALSE;
+    pdf_operators->num_glyphs = 0;
 }
 
 cairo_status_t
@@ -109,13 +110,15 @@ _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operato
  * operations (eg changing patterns).
  *
  */
-cairo_int_status_t
+cairo_status_t
 _cairo_pdf_operators_flush (cairo_pdf_operators_t	 *pdf_operators)
 {
-    if (pdf_operators->in_text)
-	_cairo_pdf_operators_end_text (pdf_operators);
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-    return CAIRO_STATUS_SUCCESS;
+    if (pdf_operators->in_text_object)
+	status = _cairo_pdf_operators_end_text (pdf_operators);
+
+    return status;
 }
 
 /* A word wrap stream can be used as a filter to do word wrapping on
@@ -801,218 +804,358 @@ _cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t 	*pdf_operators,
 					     operator);
 }
 
-static cairo_int_status_t
-_cairo_pdf_operators_end_text (cairo_pdf_operators_t    *pdf_operators)
+#define GLYPH_POSITION_TOLERANCE 0.001
+
+/* Emit the string of glyphs using the 'TJ' operator.
+ *
+ * The TJ operator takes an array of strings of glyphs. Each string of
+ * glyphs is displayed using the glyph advances of each glyph to
+ * position the glyphs. A relative adjustment to the glyph advance may
+ * be specified by including the adjustment between two strings. The
+ * adjustment is in units of text space * -1000.
+ */
+static cairo_status_t
+_cairo_pdf_operators_emit_glyph_string_with_positioning (
+    cairo_pdf_operators_t   *pdf_operators,
+    cairo_output_stream_t   *stream)
 {
-    _cairo_output_stream_printf (pdf_operators->stream, "\nET\n");
+    int i;
+
+    _cairo_output_stream_printf (stream, "[<");
+    for (i = 0; i < pdf_operators->num_glyphs; i++) {
+	if (i != 0 &&
+	    pdf_operators->glyphs[i].x_position != pdf_operators->cur_x)
+	{
+	    double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x;
+	    int rounded_delta;
+
+	    delta = -1000.0*delta;
+	    /* As the delta is in 1/1000 of a unit of text space,
+	     * rounding to an integer should still provide sufficient
+	     * precision. We round the delta before adding to Tm_x so
+	     * that we keep track of the accumulated rounding error in
+	     * the PDF interpreter and compensate for it when
+	     * calculating subsequent deltas.
+	     */
+	    rounded_delta = _cairo_lround (delta);
+	    if (rounded_delta != 0) {
+		_cairo_output_stream_printf (stream,
+					     ">%d<",
+					     rounded_delta);
+	    }
 
-    pdf_operators->in_text = FALSE;
+	    /* Convert the rounded delta back to text
+	     * space before adding to the current text
+	     * position. */
+	    delta = rounded_delta/-1000.0;
+	    pdf_operators->cur_x += delta;
+	}
 
-    return _cairo_output_stream_get_status (pdf_operators->stream);
-}
+	_cairo_output_stream_printf (stream,
+				     "%0*x",
+				     pdf_operators->hex_width,
+				     pdf_operators->glyphs[i].glyph_index);
+	pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
+    }
+    _cairo_output_stream_printf (stream, ">]TJ\n");
 
-#define GLYPH_POSITION_TOLERANCE 0.001
+    return _cairo_output_stream_get_status (stream);
+}
 
-cairo_int_status_t
-_cairo_pdf_operators_show_glyphs (cairo_pdf_operators_t		*pdf_operators,
-				  cairo_glyph_t			*glyphs,
-				  int				 num_glyphs,
-				  cairo_scaled_font_t		*scaled_font)
+static cairo_status_t
+_cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t    *pdf_operators)
 {
-    unsigned int current_subset_id = (unsigned int)-1;
-    cairo_scaled_font_subsets_glyph_t subset_glyph;
-    cairo_bool_t diagonal, in_TJ;
-    cairo_status_t status, status_ignored;
-    double Tlm_x = 0, Tlm_y = 0;
-    double Tm_x = 0, y;
-    int i, hex_width;
     cairo_output_stream_t *word_wrap_stream;
+    cairo_status_t status;
 
-    for (i = 0; i < num_glyphs; i++)
-	cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &glyphs[i].x, &glyphs[i].y);
+    if (pdf_operators->num_glyphs == 0)
+	return 0;
 
     word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 72);
     status = _cairo_output_stream_get_status (word_wrap_stream);
     if (status)
 	return _cairo_output_stream_destroy (word_wrap_stream);
 
-    if (pdf_operators->in_text == FALSE) {
-	_cairo_output_stream_printf (word_wrap_stream,
-				     "BT\n");
+    status = _cairo_pdf_operators_emit_glyph_string_with_positioning (
+	pdf_operators, word_wrap_stream);
+
+    pdf_operators->num_glyphs = 0;
+    status = _cairo_output_stream_destroy (word_wrap_stream);
+    if (status)
+	return status;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_operators_add_glyph (cairo_pdf_operators_t             *pdf_operators,
+				cairo_scaled_font_subsets_glyph_t *glyph,
+				cairo_matrix_t                    *font_matrix_inverse,
+				double 			           x_position)
+{
+    double x, y;
+
+    x = glyph->x_advance;
+    y = glyph->y_advance;
+    if (glyph->is_scaled)
+	cairo_matrix_transform_distance (font_matrix_inverse, &x, &y);
+
+    pdf_operators->glyphs[pdf_operators->num_glyphs].x_position = x_position;
+    pdf_operators->glyphs[pdf_operators->num_glyphs].glyph_index = glyph->subset_glyph_index;
+    pdf_operators->glyphs[pdf_operators->num_glyphs].x_advance = x;
+    pdf_operators->num_glyphs++;
+    if (pdf_operators->num_glyphs == PDF_GLYPH_BUFFER_SIZE)
+	return _cairo_pdf_operators_flush_glyphs (pdf_operators);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Use 'Tm' operator to set the PDF text matrix. */
+static cairo_status_t
+_cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t  *pdf_operators,
+				      cairo_matrix_t         *matrix)
+{
+    cairo_matrix_t inverse;
+    cairo_status_t status;
+
+    /* We require the matrix to be invertable. */
+    inverse = *matrix;
+    status = cairo_matrix_invert (&inverse);
+    if (status)
+	return status;
+
+    pdf_operators->text_matrix = *matrix;
+    pdf_operators->cur_x = 0;
+    pdf_operators->cur_y = 0;
+    _cairo_output_stream_printf (pdf_operators->stream,
+				 "%f %f %f %f %f %f Tm\n",
+				 pdf_operators->text_matrix.xx,
+				 pdf_operators->text_matrix.yx,
+				 pdf_operators->text_matrix.xy,
+				 pdf_operators->text_matrix.yy,
+				 pdf_operators->text_matrix.x0,
+				 pdf_operators->text_matrix.y0);
+
+    pdf_operators->cairo_to_pdftext = *matrix;
+    status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext,
+			   &pdf_operators->cairo_to_pdf,
+			   &pdf_operators->cairo_to_pdftext);
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+/* Set the translation components of the PDF text matrix to x, y. The
+ * 'Td' operator is used to transform the text matrix.
+ */
+static cairo_status_t
+_cairo_pdf_operators_set_text_position (cairo_pdf_operators_t  *pdf_operators,
+					double 			x,
+					double 			y)
+{
+    cairo_matrix_t translate, inverse;
+    cairo_status_t status;
+
+    /* The Td operator transforms the text_matrix with:
+     *
+     *   text_matrix' = T x text_matrix
+     *
+     * where T is a translation matrix with the translation components
+     * set to the Td operands tx and ty.
+     */
+    inverse = pdf_operators->text_matrix;
+    status = cairo_matrix_invert (&inverse);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    pdf_operators->text_matrix.x0 = x;
+    pdf_operators->text_matrix.y0 = y;
+    cairo_matrix_multiply (&translate, &pdf_operators->text_matrix, &inverse);
+    _cairo_output_stream_printf (pdf_operators->stream,
+				 "%f %f Td\n",
+				 translate.x0,
+				 translate.y0);
+    pdf_operators->cur_x = 0;
+    pdf_operators->cur_y = 0;
+
+    pdf_operators->cairo_to_pdftext = pdf_operators->text_matrix;
+    status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext,
+			   &pdf_operators->cairo_to_pdf,
+			   &pdf_operators->cairo_to_pdftext);
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+/* Select the font using the 'Tf' operator. The font size is set to 1
+ * as we use the 'Tm' operator to set the font scale.
+ */
+static cairo_status_t
+_cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t             *pdf_operators,
+				      cairo_scaled_font_subsets_glyph_t *subset_glyph)
+{
+    cairo_status_t status;
+
+    _cairo_output_stream_printf (pdf_operators->stream,
+				 "/f-%d-%d 1 Tf\n",
+				 subset_glyph->font_id,
+				 subset_glyph->subset_id);
+    if (pdf_operators->use_font_subset) {
+	status = pdf_operators->use_font_subset (subset_glyph->font_id,
+						 subset_glyph->subset_id,
+						 pdf_operators->use_font_subset_closure);
+	if (status)
+	    return status;
     }
+    pdf_operators->font_id = subset_glyph->font_id;
+    pdf_operators->subset_id = subset_glyph->subset_id;
 
-    if (scaled_font->scale.xy == 0.0 &&
-        scaled_font->scale.yx == 0.0)
-        diagonal = TRUE;
+    if (subset_glyph->is_composite)
+	pdf_operators->hex_width = 4;
     else
-        diagonal = FALSE;
+	pdf_operators->hex_width = 2;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_operators_begin_text (cairo_pdf_operators_t    *pdf_operators)
+{
+    _cairo_output_stream_printf (pdf_operators->stream, "BT\n");
+
+    pdf_operators->in_text_object = TRUE;
+    pdf_operators->num_glyphs = 0;
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+static cairo_status_t
+_cairo_pdf_operators_end_text (cairo_pdf_operators_t    *pdf_operators)
+{
+    _cairo_pdf_operators_flush_glyphs (pdf_operators);
+    _cairo_output_stream_printf (pdf_operators->stream, "ET\n");
+
+    pdf_operators->in_text_object = FALSE;
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+/* Compare the scale components of two matrices. The translation
+ * components are ignored. */
+static cairo_bool_t
+_cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b)
+{
+    return (a->xx == b->xx &&
+	    a->xy == b->xy &&
+	    a->yx == b->yx &&
+	    a->yy == b->yy);
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_show_glyphs (cairo_pdf_operators_t	*pdf_operators,
+				  cairo_glyph_t		*glyphs,
+				  int			 num_glyphs,
+				  cairo_scaled_font_t	*scaled_font)
+{
+    cairo_scaled_font_subsets_glyph_t subset_glyph;
+    cairo_status_t status;
+    int i;
+    cairo_matrix_t text_matrix, invert_y_axis, font_matrix_inverse;
+    double x, y;
+    cairo_bool_t new_text_object = FALSE;
+
+    if (num_glyphs <= 0)
+	return CAIRO_STATUS_SUCCESS;
+
+    font_matrix_inverse = scaled_font->font_matrix;
+    status = cairo_matrix_invert (&font_matrix_inverse);
+    if (status == CAIRO_STATUS_INVALID_MATRIX)
+	return CAIRO_STATUS_SUCCESS;
+    if (status)
+	return status;
+
+    if (pdf_operators->in_text_object == FALSE) {
+	_cairo_pdf_operators_begin_text (pdf_operators);
+
+	/* Force Tm and Tf to be emitted when starting a new text
+	 * object.*/
+	new_text_object = TRUE;
+    }
+
+    cairo_matrix_init_scale (&invert_y_axis, 1, -1);
+    text_matrix = scaled_font->scale;
+
+    /* Invert y axis in font space  */
+    cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis);
+
+    /* Invert y axis in device space  */
+    cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix);
+
+    if (new_text_object ||
+	! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix))
+    {
+	_cairo_pdf_operators_flush_glyphs (pdf_operators);
+	x = glyphs[0].x;
+	y = glyphs[0].y;
+	cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y);
+	text_matrix.x0 = x;
+	text_matrix.y0 = y;
+	status = _cairo_pdf_operators_set_text_matrix (pdf_operators, &text_matrix);
+	if (status == CAIRO_STATUS_INVALID_MATRIX)
+	    return CAIRO_STATUS_SUCCESS;
+	if (status)
+	    return status;
+    }
 
-    in_TJ = FALSE;
     for (i = 0; i < num_glyphs; i++) {
         status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
                                                        scaled_font, glyphs[i].index,
                                                        &subset_glyph);
-	if (status) {
-	    status_ignored = _cairo_output_stream_destroy (word_wrap_stream);
+	if (status)
             return status;
-	}
 
-        if (subset_glyph.is_composite)
-            hex_width = 4;
-        else
-            hex_width = 2;
-
-        if (subset_glyph.is_scaled == FALSE) {
-            y = 0.0;
-            cairo_matrix_transform_distance (&scaled_font->scale,
-                                             &subset_glyph.x_advance,
-                                             &y);
-        }
-
-	if (subset_glyph.subset_id != current_subset_id) {
-            if (in_TJ) {
-                _cairo_output_stream_printf (word_wrap_stream, ">] TJ\n");
-                in_TJ = FALSE;
-            }
-	    _cairo_output_stream_printf (word_wrap_stream,
-					 "/f-%d-%d 1 Tf\n",
-					 subset_glyph.font_id,
-					 subset_glyph.subset_id);
-	    if (pdf_operators->use_font_subset) {
-		status = pdf_operators->use_font_subset (subset_glyph.font_id,
-							 subset_glyph.subset_id,
-							 pdf_operators->use_font_subset_closure);
-		if (status) {
-		    status_ignored = _cairo_output_stream_destroy (word_wrap_stream);
-		    return status;
-		}
-	    }
-        }
-
-        if (subset_glyph.subset_id != current_subset_id || !diagonal) {
-            _cairo_output_stream_printf (word_wrap_stream,
-                                         "%f %f %f %f %f %f Tm\n",
-                                         scaled_font->scale.xx,
-                                         -scaled_font->scale.yx,
-                                         -scaled_font->scale.xy,
-                                         scaled_font->scale.yy,
-                                         glyphs[i].x,
-                                         glyphs[i].y);
-            current_subset_id = subset_glyph.subset_id;
-            Tlm_x = glyphs[i].x;
-            Tlm_y = glyphs[i].y;
-            Tm_x = Tlm_x;
-        }
-
-        if (diagonal) {
-            if (i < num_glyphs - 1 &&
-                fabs((glyphs[i].y - glyphs[i+1].y)/scaled_font->scale.yy) < GLYPH_POSITION_TOLERANCE &&
-                fabs((glyphs[i].x - glyphs[i+1].x)/scaled_font->scale.xx) < 10)
-            {
-                if (!in_TJ) {
-                    if (i != 0) {
-                        _cairo_output_stream_printf (word_wrap_stream,
-                                                     "%f %f Td\n",
-                                                     (glyphs[i].x - Tlm_x)/scaled_font->scale.xx,
-                                                     (glyphs[i].y - Tlm_y)/scaled_font->scale.yy);
-
-                        Tlm_x = glyphs[i].x;
-                        Tlm_y = glyphs[i].y;
-                        Tm_x = Tlm_x;
-                    }
-                    _cairo_output_stream_printf (word_wrap_stream,
-                                                 "[<%0*x",
-                                                 hex_width,
-                                                 subset_glyph.subset_glyph_index);
-                    Tm_x += subset_glyph.x_advance;
-                    in_TJ = TRUE;
-                } else {
-                    if (fabs((glyphs[i].x - Tm_x)/scaled_font->scale.xx) > GLYPH_POSITION_TOLERANCE) {
-                        double delta = glyphs[i].x - Tm_x;
-			int rounded_delta;
-
-			delta = -1000.0*delta/scaled_font->scale.xx;
-			/* As the delta is in 1/1000 of a unit of text
-			 * space, rounding to an integer should still
-			 * provide sufficient precision. We round the
-			 * delta before adding to Tm_x so that we keep
-			 * track of the accumulated rounding error in
-			 * the PDF interpreter and compensate for it
-			 * when calculating subsequent deltas.
-			 */
-			rounded_delta = _cairo_lround (delta);
-			if (rounded_delta != 0) {
-			    _cairo_output_stream_printf (word_wrap_stream,
-							 "> %d <",
-							 rounded_delta);
-			}
-
-			/* Convert the rounded delta back to cairo
-			 * space before adding to the current text
-			 * position. */
-			delta = rounded_delta*scaled_font->scale.xx/-1000.0;
-                        Tm_x += delta;
-                    }
-                    _cairo_output_stream_printf (word_wrap_stream,
-                                                 "%0*x",
-                                                 hex_width,
-                                                 subset_glyph.subset_glyph_index);
-                    Tm_x += subset_glyph.x_advance;
-                }
-            }
-            else
-            {
-                if (in_TJ) {
-                    if (fabs((glyphs[i].x - Tm_x)/scaled_font->scale.xx) > GLYPH_POSITION_TOLERANCE) {
-                        double delta = glyphs[i].x - Tm_x;
-			int rounded_delta;
-
-			delta = -1000.0*delta/scaled_font->scale.xx;
-			rounded_delta = _cairo_lround (delta);
-			if (rounded_delta != 0) {
-			    _cairo_output_stream_printf (word_wrap_stream,
-							 "> %d <",
-							 rounded_delta);
-			}
-			delta = rounded_delta*scaled_font->scale.xx/-1000.0;
-                        Tm_x += delta;
-                    }
-                    _cairo_output_stream_printf (word_wrap_stream,
-                                                 "%0*x>] TJ\n",
-                                                 hex_width,
-                                                 subset_glyph.subset_glyph_index);
-                    Tm_x += subset_glyph.x_advance;
-                    in_TJ = FALSE;
-                } else {
-                    if (i != 0) {
-                        _cairo_output_stream_printf (word_wrap_stream,
-                                                     "%f %f Td ",
-                                                     (glyphs[i].x - Tlm_x)/scaled_font->scale.xx,
-                                                     (glyphs[i].y - Tlm_y)/scaled_font->scale.yy);
-                        Tlm_x = glyphs[i].x;
-                        Tlm_y = glyphs[i].y;
-                        Tm_x = Tlm_x;
-                    }
-                    _cairo_output_stream_printf (word_wrap_stream,
-                                                 "<%0*x> Tj ",
-                                                 hex_width,
-                                                 subset_glyph.subset_glyph_index);
-                    Tm_x += subset_glyph.x_advance;
-                }
-            }
-        } else {
-            _cairo_output_stream_printf (word_wrap_stream,
-                                         "<%0*x> Tj\n",
-                                         hex_width,
-                                         subset_glyph.subset_glyph_index);
-        }
-    }
+	if (new_text_object ||
+	    pdf_operators->font_id != subset_glyph.font_id ||
+	    pdf_operators->subset_id != subset_glyph.subset_id)
+	{
+	    _cairo_pdf_operators_flush_glyphs (pdf_operators);
+	    _cairo_pdf_operators_set_font_subset (pdf_operators, &subset_glyph);
+	    new_text_object = FALSE;
+	}
 
-    pdf_operators->in_text = TRUE;
+	x = glyphs[i].x;
+	y = glyphs[i].y;
+	cairo_matrix_transform_point (&pdf_operators->cairo_to_pdftext, &x, &y);
+
+	/* The TJ operator for displaying text strings can only set
+	 * the horizontal position of the glyphs. If the y position
+	 * (in text space) changes, use the Td operator to change the
+	 * current position to the next glyph. We also use the Td
+	 * operator to move the current position if the horizontal
+	 * position changes by more than 10 (in text space
+	 * units). This is becauses the horizontal glyph positioning
+	 * in the TJ operator is intended for kerning and there may be
+	 * PDF consumers that do not handle very large position
+	 * adjustments in TJ.
+	 */
+	if (fabs(x - pdf_operators->cur_x) > 10 ||
+	    fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE)
+	{
+	    _cairo_pdf_operators_flush_glyphs (pdf_operators);
+
+	    x = glyphs[i].x;
+	    y = glyphs[i].y;
+	    cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y);
+	    _cairo_pdf_operators_set_text_position (pdf_operators, x, y);
+	    x = 0.0;
+	    y = 0.0;
+	}
 
-    status = _cairo_output_stream_destroy (word_wrap_stream);
-    if (status)
-	return status;
+	_cairo_pdf_operators_add_glyph (pdf_operators,
+					&subset_glyph,
+					&font_matrix_inverse,
+					x);
+    }
 
     return _cairo_output_stream_get_status (pdf_operators->stream);
 }
commit f3d457db0c5cf79264bf48f0bef9209d68bd259f
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Jun 4 22:36:10 2008 +0930

    Store y_advance in cairo_scaled_font_subsets_glyph_t

diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h
index 235343e..da64f7f 100644
--- a/src/cairo-scaled-font-subsets-private.h
+++ b/src/cairo-scaled-font-subsets-private.h
@@ -46,6 +46,7 @@ typedef struct _cairo_scaled_font_subsets_glyph {
     cairo_bool_t is_scaled;
     cairo_bool_t is_composite;
     double       x_advance;
+    double       y_advance;
 } cairo_scaled_font_subsets_glyph_t;
 
 /**
diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
index 79bc097..133f6b5 100644
--- a/src/cairo-scaled-font-subsets.c
+++ b/src/cairo-scaled-font-subsets.c
@@ -92,6 +92,7 @@ typedef struct _cairo_sub_font_glyph {
     unsigned int subset_id;
     unsigned int subset_glyph_index;
     double       x_advance;
+    double       y_advance;
 } cairo_sub_font_glyph_t;
 
 typedef struct _cairo_sub_font_collection {
@@ -137,7 +138,8 @@ static cairo_sub_font_glyph_t *
 _cairo_sub_font_glyph_create (unsigned long	scaled_font_glyph_index,
 			      unsigned int	subset_id,
 			      unsigned int	subset_glyph_index,
-                              double            x_advance)
+                              double            x_advance,
+                              double            y_advance)
 {
     cairo_sub_font_glyph_t *sub_font_glyph;
 
@@ -151,6 +153,7 @@ _cairo_sub_font_glyph_create (unsigned long	scaled_font_glyph_index,
     sub_font_glyph->subset_id = subset_id;
     sub_font_glyph->subset_glyph_index = subset_glyph_index;
     sub_font_glyph->x_advance = x_advance;
+    sub_font_glyph->y_advance = y_advance;
 
     return sub_font_glyph;
 }
@@ -309,6 +312,7 @@ _cairo_sub_font_lookup_glyph (cairo_sub_font_t	                *sub_font,
         subset_glyph->is_scaled = sub_font->is_scaled;
         subset_glyph->is_composite = sub_font->is_composite;
         subset_glyph->x_advance = sub_font_glyph->x_advance;
+        subset_glyph->y_advance = sub_font_glyph->y_advance;
 
         return TRUE;
     }
@@ -352,7 +356,8 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t	*sub_font,
         sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index,
 						       sub_font->current_subset,
 						       sub_font->num_glyphs_in_current_subset,
-                                                       scaled_glyph->metrics.x_advance);
+                                                       scaled_glyph->metrics.x_advance,
+                                                       scaled_glyph->metrics.y_advance);
 	if (sub_font_glyph == NULL)
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
@@ -382,6 +387,7 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t	*sub_font,
     subset_glyph->is_scaled = sub_font->is_scaled;
     subset_glyph->is_composite = sub_font->is_composite;
     subset_glyph->x_advance = sub_font_glyph->x_advance;
+    subset_glyph->y_advance = sub_font_glyph->y_advance;
 
     return CAIRO_STATUS_SUCCESS;
 }
commit 6139fdc1073c0fe1bc5a3809f15c2ee8b5028469
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Tue Jun 3 20:56:11 2008 +0930

    PDF: Don't emit a new text object for every call to show_glyphs
    
    A text object is a series of text operations enclosed in a 'BT'/'ET'
    pair.
    
    After a call to _cairo_pdf_operators_show_glyphs() the closing 'ET'
    will not be emitted. This allows subsequent calls to show_glyphs() to
    emit text into the same text object. A call to any other operator or
    _cairo_pdf_operators_flush() will close the text object.

diff --git a/src/cairo-pdf-operators-private.h b/src/cairo-pdf-operators-private.h
index 8707591..3486d13 100644
--- a/src/cairo-pdf-operators-private.h
+++ b/src/cairo-pdf-operators-private.h
@@ -55,6 +55,7 @@ typedef struct _cairo_pdf_operators {
     cairo_scaled_font_subsets_t *font_subsets;
     cairo_pdf_operators_use_font_subset_t use_font_subset;
     void *use_font_subset_closure;
+    cairo_bool_t in_text;
 } cairo_pdf_operators_t;
 
 cairo_private void
diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c
index 84363f5..d4b9a3c 100644
--- a/src/cairo-pdf-operators.c
+++ b/src/cairo-pdf-operators.c
@@ -47,6 +47,10 @@
 
 #include <ctype.h>
 
+static cairo_int_status_t
+_cairo_pdf_operators_end_text (cairo_pdf_operators_t    *pdf_operators);
+
+
 void
 _cairo_pdf_operators_init (cairo_pdf_operators_t	*pdf_operators,
 			   cairo_output_stream_t	*stream,
@@ -58,6 +62,7 @@ _cairo_pdf_operators_init (cairo_pdf_operators_t	*pdf_operators,
     pdf_operators->font_subsets = font_subsets;
     pdf_operators->use_font_subset = NULL;
     pdf_operators->use_font_subset_closure = NULL;
+    pdf_operators->in_text = FALSE;
 }
 
 cairo_status_t
@@ -107,6 +112,9 @@ _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operato
 cairo_int_status_t
 _cairo_pdf_operators_flush (cairo_pdf_operators_t	 *pdf_operators)
 {
+    if (pdf_operators->in_text)
+	_cairo_pdf_operators_end_text (pdf_operators);
+
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -793,6 +801,16 @@ _cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t 	*pdf_operators,
 					     operator);
 }
 
+static cairo_int_status_t
+_cairo_pdf_operators_end_text (cairo_pdf_operators_t    *pdf_operators)
+{
+    _cairo_output_stream_printf (pdf_operators->stream, "\nET\n");
+
+    pdf_operators->in_text = FALSE;
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
 #define GLYPH_POSITION_TOLERANCE 0.001
 
 cairo_int_status_t
@@ -818,8 +836,10 @@ _cairo_pdf_operators_show_glyphs (cairo_pdf_operators_t		*pdf_operators,
     if (status)
 	return _cairo_output_stream_destroy (word_wrap_stream);
 
-    _cairo_output_stream_printf (word_wrap_stream,
-				 "BT\n");
+    if (pdf_operators->in_text == FALSE) {
+	_cairo_output_stream_printf (word_wrap_stream,
+				     "BT\n");
+    }
 
     if (scaled_font->scale.xy == 0.0 &&
         scaled_font->scale.yx == 0.0)
@@ -988,8 +1008,7 @@ _cairo_pdf_operators_show_glyphs (cairo_pdf_operators_t		*pdf_operators,
         }
     }
 
-    _cairo_output_stream_printf (word_wrap_stream,
-				 "ET\n");
+    pdf_operators->in_text = TRUE;
 
     status = _cairo_output_stream_destroy (word_wrap_stream);
     if (status)
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index c36cd65..ddabc03 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -4799,6 +4799,16 @@ _cairo_pdf_surface_show_glyphs (void			*abstract_surface,
 	if (status)
 	    return status;
 
+	/* Each call to show_glyphs() with a transclucent pattern must
+	 * be in a separate text object otherwise overlapping text
+	 * from separate calls to show_glyphs will not composite with
+	 * each other. */
+	if (! _cairo_pattern_is_opaque (source)) {
+	    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	    if (status)
+		return status;
+	}
+
 	status = _cairo_pdf_operators_show_glyphs (&surface->pdf_operators,
 						   glyphs,
 						   num_glyphs,
commit 6258f1a4e29e0457465e83bd5be1900a10c75473
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Tue Jun 3 20:55:30 2008 +0930

    PDF: Remember the current color
    
    Don't emit the set fill or stroke color operator if the required fill
    or stroke color is already selected.

diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index 98ba217..711afb5 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -153,6 +153,13 @@ struct _cairo_pdf_surface {
 
     cairo_bool_t force_fallbacks;
 
+    cairo_bool_t current_pattern_is_solid_color;
+    cairo_bool_t current_color_is_stroke;
+    double current_color_red;
+    double current_color_green;
+    double current_color_blue;
+    double current_color_alpha;
+
     cairo_surface_t *paginated_surface;
 };
 
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index cf36170..c36cd65 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -281,6 +281,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t	*output,
 
     surface->force_fallbacks = FALSE;
     surface->select_pattern_gstate_saved = FALSE;
+    surface->current_pattern_is_solid_color = FALSE;
 
     _cairo_pdf_operators_init (&surface->pdf_operators,
 			       surface->output,
@@ -863,6 +864,7 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t	*surface,
     surface->pdf_stream.self = self;
     surface->pdf_stream.length = length;
     surface->pdf_stream.compressed = compressed;
+    surface->current_pattern_is_solid_color = FALSE;
 
     _cairo_output_stream_printf (surface->output,
 				 "%d 0 obj\n"
@@ -998,6 +1000,7 @@ _cairo_pdf_surface_open_group (cairo_pdf_surface_t  *surface,
     assert (surface->group_stream.active == FALSE);
 
     surface->group_stream.active = TRUE;
+    surface->current_pattern_is_solid_color = FALSE;
 
     surface->group_stream.mem_stream = _cairo_memory_stream_create ();
 
@@ -2592,10 +2595,6 @@ _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
     cairo_bool_t is_solid_color = FALSE;
     cairo_color_t *solid_color;
 
-    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
-    if (status)
-	return status;
-
     if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
 	cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
 
@@ -2615,24 +2614,51 @@ _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
     }
 
     if (is_solid_color) {
-	status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha);
-	if (status)
-	    return status;
+	if (surface->current_pattern_is_solid_color == FALSE ||
+	    surface->current_color_red != solid_color->red ||
+	    surface->current_color_green != solid_color->green ||
+	    surface->current_color_blue != solid_color->blue ||
+	    surface->current_color_is_stroke != is_stroke)
+	{
+	    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	    if (status)
+		return status;
 
-	_cairo_output_stream_printf (surface->output,
-				     "%f %f %f ",
-				     solid_color->red,
-				     solid_color->green,
-				     solid_color->blue);
+	    _cairo_output_stream_printf (surface->output,
+					 "%f %f %f ",
+					 solid_color->red,
+					 solid_color->green,
+					 solid_color->blue);
 
-	if (is_stroke)
-            _cairo_output_stream_printf (surface->output, "RG ");
-        else
-            _cairo_output_stream_printf (surface->output, "rg ");
+	    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\n",
-                                     alpha);
+	    surface->current_color_red = solid_color->red;
+	    surface->current_color_green = solid_color->green;
+	    surface->current_color_blue = solid_color->blue;
+	    surface->current_color_is_stroke = is_stroke;
+	}
+
+	if (surface->current_pattern_is_solid_color == FALSE ||
+	    surface->current_color_alpha != solid_color->alpha)
+	{
+	    status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha);
+	    if (status)
+		return status;
+
+	    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	    if (status)
+		return status;
+
+	    _cairo_output_stream_printf (surface->output,
+					 "/a%d gs\n",
+					 alpha);
+	    surface->current_color_alpha = solid_color->alpha;
+	}
+
+	surface->current_pattern_is_solid_color = TRUE;
     } else {
 	status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
 	if (status)
@@ -2642,6 +2668,10 @@ _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
 	if (status)
 	    return status;
 
+	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	if (status)
+	    return status;
+
 	/* fill-stroke calls select_pattern twice. Don't save if the
 	 * gstate is already saved. */
 	if (!surface->select_pattern_gstate_saved)
@@ -2660,6 +2690,7 @@ _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
 				     "/a%d gs\n",
 				     alpha);
 	surface->select_pattern_gstate_saved = TRUE;
+	surface->current_pattern_is_solid_color = FALSE;
     }
 
     return _cairo_output_stream_get_status (surface->output);
@@ -2736,6 +2767,8 @@ _cairo_pdf_surface_intersect_clip_path (void			*abstract_surface,
 	    return status;
 
 	_cairo_output_stream_printf (surface->output, "Q q\n");
+	surface->current_pattern_is_solid_color = FALSE;
+
 	return CAIRO_STATUS_SUCCESS;
     }
 
commit fd42b74a4f35154d7c9a03b4f248801cd0b7c339
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Tue Jun 3 20:55:03 2008 +0930

    Add _cairo_pdf_operators_flush()
    
    The optimizations planned for pdf-operators will mean that it will no
    longer emit complete operations on each call to
    fill/stroke/show_glyphs. For example a call to _show_glyphs() may not
    finish the text operation to allow a subsequent call to _show_glyphs()
    to be merged into the same text object.
    
    A flush function is required to force pdf_operators to complete the
    current operation before the pdf surface can emit any pdf operators.

diff --git a/src/cairo-pdf-operators-private.h b/src/cairo-pdf-operators-private.h
index e66839c..8707591 100644
--- a/src/cairo-pdf-operators-private.h
+++ b/src/cairo-pdf-operators-private.h
@@ -63,7 +63,7 @@ _cairo_pdf_operators_init (cairo_pdf_operators_t       *pdf_operators,
 			   cairo_matrix_t 	       *cairo_to_pdf,
 			   cairo_scaled_font_subsets_t *font_subsets);
 
-cairo_private void
+cairo_private cairo_status_t
 _cairo_pdf_operators_fini (cairo_pdf_operators_t       *pdf_operators);
 
 cairo_private void
@@ -81,6 +81,9 @@ _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operato
 					      cairo_matrix_t 	    *cairo_to_pdf);
 
 cairo_private cairo_int_status_t
+_cairo_pdf_operators_flush (cairo_pdf_operators_t	 *pdf_operators);
+
+cairo_private cairo_int_status_t
 _cairo_pdf_operators_clip (cairo_pdf_operators_t 	*pdf_operators,
 			   cairo_path_fixed_t		*path,
 			   cairo_fill_rule_t		 fill_rule);
diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c
index 1198b4e..84363f5 100644
--- a/src/cairo-pdf-operators.c
+++ b/src/cairo-pdf-operators.c
@@ -60,9 +60,10 @@ _cairo_pdf_operators_init (cairo_pdf_operators_t	*pdf_operators,
     pdf_operators->use_font_subset_closure = NULL;
 }
 
-void
+cairo_status_t
 _cairo_pdf_operators_fini (cairo_pdf_operators_t	*pdf_operators)
 {
+    return _cairo_pdf_operators_flush (pdf_operators);
 }
 
 void
@@ -74,6 +75,10 @@ _cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t		     *pdf
     pdf_operators->use_font_subset_closure = closure;
 }
 
+/* Change the output stream to a different stream.
+ * _cairo_pdf_operators_flush() should always be called before calling
+ * this function.
+ */
 void
 _cairo_pdf_operators_set_stream (cairo_pdf_operators_t	 *pdf_operators,
 				 cairo_output_stream_t   *stream)
@@ -88,6 +93,23 @@ _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operato
     pdf_operators->cairo_to_pdf = *cairo_to_pdf;
 }
 
+/* Finish writing out any pending commands to the stream. This
+ * function must be called by the surface before emitting anything
+ * into the PDF stream.
+ *
+ * pdf_operators may leave the emitted PDF for some operations
+ * unfinished in case subsequent operations can be merged. This
+ * function will finish off any incomplete operation so the stream
+ * will be in a state where the surface may emit it's own PDF
+ * operations (eg changing patterns).
+ *
+ */
+cairo_int_status_t
+_cairo_pdf_operators_flush (cairo_pdf_operators_t	 *pdf_operators)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
 /* 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
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 82b68b2..cf36170 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -904,6 +904,10 @@ _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface)
     if (! surface->pdf_stream.active)
 	return CAIRO_STATUS_SUCCESS;
 
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status)
+	return status;
+
     if (surface->pdf_stream.compressed) {
 	status = _cairo_output_stream_destroy (surface->output);
 	surface->output = surface->pdf_stream.old_output;
@@ -1045,6 +1049,10 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface,
     assert (surface->pdf_stream.active == FALSE);
     assert (surface->group_stream.active == TRUE);
 
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status)
+	return status;
+
     if (surface->compress_content) {
 	status = _cairo_output_stream_destroy (surface->group_stream.stream);
 	surface->group_stream.stream = NULL;
@@ -1128,6 +1136,10 @@ _cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface)
     assert (surface->pdf_stream.active == TRUE);
     assert (surface->group_stream.active == FALSE);
 
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status)
+	return status;
+
     _cairo_output_stream_printf (surface->output, "Q\n");
     status = _cairo_pdf_surface_close_stream (surface);
     if (status)
@@ -1193,7 +1205,10 @@ _cairo_pdf_surface_finish (void *abstract_surface)
 				 "%%%%EOF\n",
 				 offset);
 
-     _cairo_pdf_operators_fini (&surface->pdf_operators);
+    status2 = _cairo_pdf_operators_fini (&surface->pdf_operators);
+    /* pdf_operators has already been flushed when the last stream was
+     * closed so we should never be writing anything here. */
+    assert(status2 == CAIRO_STATUS_SUCCESS);
 
     /* close any active streams still open due to fatal errors */
     status2 = _cairo_pdf_surface_close_stream (surface);
@@ -2577,6 +2592,10 @@ _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
     cairo_bool_t is_solid_color = FALSE;
     cairo_color_t *solid_color;
 
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status)
+	return status;
+
     if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
 	cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
 
@@ -2646,12 +2665,21 @@ _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
     return _cairo_output_stream_get_status (surface->output);
 }
 
-static void
+static cairo_int_status_t
 _cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface)
 {
-    if (surface->select_pattern_gstate_saved)
+    cairo_int_status_t status;
+
+    if (surface->select_pattern_gstate_saved) {
+	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	if (status)
+	    return status;
+
 	_cairo_output_stream_printf (surface->output, "Q\n");
+    }
     surface->select_pattern_gstate_saved = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_int_status_t
@@ -2700,8 +2728,13 @@ _cairo_pdf_surface_intersect_clip_path (void			*abstract_surface,
 					cairo_antialias_t	antialias)
 {
     cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
 
     if (path == NULL) {
+	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	if (status)
+	    return status;
+
 	_cairo_output_stream_printf (surface->output, "Q q\n");
 	return CAIRO_STATUS_SUCCESS;
     }
@@ -3759,7 +3792,9 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t	*surface,
 				     "0 0 %f %f re f\n",
 				     surface->width, surface->height);
 
-	_cairo_pdf_surface_unselect_pattern (surface);
+	status = _cairo_pdf_surface_unselect_pattern (surface);
+	if (status)
+	    return status;
     }
 
     status = _cairo_pdf_surface_close_group (surface, &mask_group);
@@ -3812,7 +3847,9 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t	*surface,
 				     "0 0 %f %f re f\n",
 				     surface->width, surface->height);
 
-	_cairo_pdf_surface_unselect_pattern (surface);
+	status = _cairo_pdf_surface_unselect_pattern (surface);
+	if (status)
+	    return status;
     }
 
     status = _cairo_pdf_surface_close_group (surface, NULL);
@@ -3910,7 +3947,10 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t     *surface,
     if (status)
 	return status;
 
-    _cairo_pdf_surface_unselect_pattern (surface);
+    status = _cairo_pdf_surface_unselect_pattern (surface);
+    if (status)
+	return status;
+
     status = _cairo_pdf_surface_close_group (surface, NULL);
 
     _cairo_pdf_surface_set_size_internal (surface,
@@ -4297,6 +4337,10 @@ _cairo_pdf_surface_paint (void			*abstract_surface,
 	if (status)
 	    return status;
 
+	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	if (status)
+	    return status;
+
 	_cairo_output_stream_printf (surface->output,
 				     "q /s%d gs /x%d Do Q\n",
 				     gstate_res.id,
@@ -4310,7 +4354,9 @@ _cairo_pdf_surface_paint (void			*abstract_surface,
 				     "0 0 %f %f re f\n",
 				     surface->width, surface->height);
 
-	_cairo_pdf_surface_unselect_pattern (surface);
+	status = _cairo_pdf_surface_unselect_pattern (surface);
+	if (status)
+	    return status;
     }
 
     return _cairo_output_stream_get_status (surface->output);
@@ -4373,6 +4419,10 @@ _cairo_pdf_surface_mask	(void			*abstract_surface,
     if (status)
 	return status;
 
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status)
+	return status;
+
     _cairo_output_stream_printf (surface->output,
 				 "q /s%d gs /x%d Do Q\n",
 				 group->group_res.id,
@@ -4441,6 +4491,10 @@ _cairo_pdf_surface_stroke (void			*abstract_surface,
 	if (status)
 	    return status;
 
+	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	if (status)
+	    return status;
+
 	_cairo_output_stream_printf (surface->output,
 				     "q /s%d gs /x%d Do Q\n",
 				     gstate_res.id,
@@ -4458,7 +4512,9 @@ _cairo_pdf_surface_stroke (void			*abstract_surface,
 	if (status)
 	    return status;
 
-	_cairo_pdf_surface_unselect_pattern (surface);
+	status = _cairo_pdf_surface_unselect_pattern (surface);
+	if (status)
+	    return status;
     }
 
     return _cairo_output_stream_get_status (surface->output);
@@ -4525,6 +4581,10 @@ _cairo_pdf_surface_fill (void			*abstract_surface,
 	if (status)
 	    return status;
 
+	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	if (status)
+	    return status;
+
 	_cairo_output_stream_printf (surface->output,
 				     "q /s%d gs /x%d Do Q\n",
 				     gstate_res.id,
@@ -4540,7 +4600,9 @@ _cairo_pdf_surface_fill (void			*abstract_surface,
 	if (status)
 	    return status;
 
-	_cairo_pdf_surface_unselect_pattern (surface);
+	status = _cairo_pdf_surface_unselect_pattern (surface);
+	if (status)
+	    return status;
     }
 
     return _cairo_output_stream_get_status (surface->output);
@@ -4627,7 +4689,9 @@ _cairo_pdf_surface_fill_stroke (void		     *abstract_surface,
     if (status)
 	return status;
 
-    _cairo_pdf_surface_unselect_pattern (surface);
+    status = _cairo_pdf_surface_unselect_pattern (surface);
+    if (status)
+	return status;
 
     return _cairo_output_stream_get_status (surface->output);
 }
@@ -4689,6 +4753,10 @@ _cairo_pdf_surface_show_glyphs (void			*abstract_surface,
 	if (status)
 	    return status;
 
+	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	if (status)
+	    return status;
+
 	_cairo_output_stream_printf (surface->output,
 				     "q /s%d gs /x%d Do Q\n",
 				     gstate_res.id,
@@ -4705,7 +4773,9 @@ _cairo_pdf_surface_show_glyphs (void			*abstract_surface,
 	if (status)
 	    return status;
 
-	_cairo_pdf_surface_unselect_pattern (surface);
+	status = _cairo_pdf_surface_unselect_pattern (surface);
+	if (status)
+	    return status;
     }
 
     return _cairo_output_stream_get_status (surface->output);
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 589607e..b4f0718 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1327,19 +1327,30 @@ _cairo_ps_surface_start_page (void *abstract_surface)
     return CAIRO_STATUS_SUCCESS;
 }
 
-static void
+static cairo_int_status_t
 _cairo_ps_surface_end_page (cairo_ps_surface_t *surface)
 {
+    cairo_int_status_t status;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status)
+	return status;
+
     _cairo_output_stream_printf (surface->stream,
 				 "Q\n");
+
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_int_status_t
 _cairo_ps_surface_show_page (void *abstract_surface)
 {
     cairo_ps_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
 
-    _cairo_ps_surface_end_page (surface);
+    status = _cairo_ps_surface_end_page (surface);
+    if (status)
+	return status;
 
     _cairo_output_stream_printf (surface->stream, "showpage\n");
 
@@ -2078,6 +2089,10 @@ _cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t  *surface,
     if (status)
 	return status;
 
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status)
+	return status;
+
     _cairo_output_stream_printf (surface->stream,
 				 "  Q\n");
     surface->content = old_content;
@@ -2835,6 +2850,10 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface,
      * different pattern. */
     cairo_status_t status;
 
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status)
+	    return status;
+
     switch (pattern->type) {
     case CAIRO_PATTERN_TYPE_SOLID:
 	_cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
@@ -2875,6 +2894,7 @@ _cairo_ps_surface_intersect_clip_path (void		   *abstract_surface,
 {
     cairo_ps_surface_t *surface = abstract_surface;
     cairo_output_stream_t *stream = surface->stream;
+    cairo_status_t status;
 
     if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
 	return CAIRO_STATUS_SUCCESS;
@@ -2885,6 +2905,10 @@ _cairo_ps_surface_intersect_clip_path (void		   *abstract_surface,
 #endif
 
     if (path == NULL) {
+	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	if (status)
+	    return status;
+
 	_cairo_output_stream_printf (stream, "Q q\n");
 	return CAIRO_STATUS_SUCCESS;
     }
@@ -2948,6 +2972,10 @@ _cairo_ps_surface_paint (void			*abstract_surface,
     if (status)
 	return status;
 
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status)
+	return status;
+
     if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
 	(source->extend == CAIRO_EXTEND_NONE ||
 	 source->extend == CAIRO_EXTEND_PAD))
@@ -3040,6 +3068,10 @@ _cairo_ps_surface_fill (void		*abstract_surface,
 	(source->extend == CAIRO_EXTEND_NONE ||
 	 source->extend == CAIRO_EXTEND_PAD))
     {
+	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	if (status)
+	    return status;
+
 	_cairo_output_stream_printf (surface->stream, "q\n");
 
 	status =  _cairo_pdf_operators_clip (&surface->pdf_operators,


More information about the cairo-commit mailing list