[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