diff options
Diffstat (limited to 'Build/source/libs/cairo/cairo-src/src/cairo-ps-surface.c')
-rw-r--r-- | Build/source/libs/cairo/cairo-src/src/cairo-ps-surface.c | 1940 |
1 files changed, 1332 insertions, 608 deletions
diff --git a/Build/source/libs/cairo/cairo-src/src/cairo-ps-surface.c b/Build/source/libs/cairo/cairo-src/src/cairo-ps-surface.c index 3b66b275d82..0a17b81afe3 100644 --- a/Build/source/libs/cairo/cairo-src/src/cairo-ps-surface.c +++ b/Build/source/libs/cairo/cairo-src/src/cairo-ps-surface.c @@ -77,6 +77,7 @@ #include "cairo-output-stream-private.h" #include "cairo-type3-glyph-surface-private.h" #include "cairo-image-info-private.h" +#include "cairo-tag-attributes-private.h" #include <stdio.h> #include <ctype.h> @@ -84,7 +85,14 @@ #include <zlib.h> #include <errno.h> -#define DEBUG_PS 0 +/* Forms are emitted at the start and stored in memory so we limit the + * total size of all forms to prevent running out of memory. If this + * limit is exceeded, surfaces that would be stored in forms are + * emitted each time the surface is used. */ +#define MAX_L2_FORM_DATA (256*1024) +#define MAX_L3_FORM_DATA (2*1024*1024) /* Assume Level 3 printers have more memory */ + +/* #define DEBUG_PS 1 */ #if DEBUG_PS #define DEBUG_FALLBACK(s) \ @@ -105,11 +113,44 @@ * * The PostScript surface is used to render cairo graphics to Adobe * PostScript files and is a multi-page vector surface backend. + * + * The following mime types are supported: %CAIRO_MIME_TYPE_JPEG, + * %CAIRO_MIME_TYPE_UNIQUE_ID, + * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + * %CAIRO_MIME_TYPE_EPS, %CAIRO_MIME_TYPE_EPS_PARAMS. + * + * Source surfaces used by the PostScript surface that have a + * %CAIRO_MIME_TYPE_UNIQUE_ID mime type will be stored in PostScript + * printer memory for the duration of the print + * job. %CAIRO_MIME_TYPE_UNIQUE_ID should only be used for small + * frequently used sources. + * + * The %CAIRO_MIME_TYPE_CCITT_FAX and %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS mime types + * are documented in [CCITT Fax Images][ccitt]. + * + * # Embedding EPS files # {#eps} + * + * Encapsulated PostScript files can be embedded in the PS output by + * setting the CAIRO_MIME_TYPE_EPS mime data on a surface to the EPS + * data and painting the surface. The EPS will be scaled and + * translated to the extents of the surface the EPS data is attached + * to. + * + * The %CAIRO_MIME_TYPE_EPS mime type requires the + * %CAIRO_MIME_TYPE_EPS_PARAMS mime data to also be provided in order + * to specify the embeddding parameters. %CAIRO_MIME_TYPE_EPS_PARAMS + * mime data must contain a string of the form "bbox=[llx lly urx + * ury]" that specifies the bounding box (in PS coordinates) of the + * EPS graphics. The parameters are: lower left x, lower left y, upper + * right x, upper right y. Normally the bbox data is identical to the + * %%%BoundingBox data in the EPS file. + * **/ /** * CAIRO_HAS_PS_SURFACE: - * + * * Defined if the PostScript surface backend is available. * This macro can be used to conditionally compile backend-specific code. * @@ -120,7 +161,30 @@ typedef enum { CAIRO_PS_COMPRESS_NONE, CAIRO_PS_COMPRESS_LZW, CAIRO_PS_COMPRESS_DEFLATE - } cairo_ps_compress_t; +} cairo_ps_compress_t; + +typedef enum { + CAIRO_EMIT_SURFACE_ANALYZE, + CAIRO_EMIT_SURFACE_EMIT, + CAIRO_EMIT_SURFACE_EMIT_FORM +} cairo_emit_surface_mode_t; + +typedef struct { + /* input params */ + cairo_surface_t *src_surface; + cairo_operator_t op; + const cairo_rectangle_int_t *src_surface_extents; + cairo_bool_t src_surface_bounded; + const cairo_rectangle_int_t *src_op_extents; /* operation extents in src space */ + cairo_filter_t filter; + cairo_bool_t stencil_mask; /* TRUE if source is to be used as a mask */ + + /* output params */ + cairo_bool_t is_image; /* returns TRUE if PS image will be emitted */ + /* FALSE if recording will be emitted */ + long approx_size; + int eod_count; +} cairo_emit_surface_params_t; static const cairo_surface_backend_t cairo_ps_surface_backend; static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend; @@ -129,6 +193,9 @@ static cairo_bool_t _cairo_ps_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle); +static void +_cairo_ps_form_emit (void *entry, void *closure); + static const cairo_ps_level_t _cairo_ps_levels[] = { CAIRO_PS_LEVEL_2, @@ -146,6 +213,8 @@ static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] = static const char *_cairo_ps_supported_mime_types[] = { CAIRO_MIME_TYPE_JPEG, + CAIRO_MIME_TYPE_CCITT_FAX, + CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, NULL }; @@ -184,6 +253,40 @@ typedef struct _cairo_page_media { } cairo_page_media_t; static void +_cairo_ps_form_init_key (cairo_ps_form_t *key) +{ + key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE, + key->unique_id, key->unique_id_length); +} + +static cairo_bool_t +_cairo_ps_form_equal (const void *key_a, const void *key_b) +{ + const cairo_ps_form_t *a = key_a; + const cairo_ps_form_t *b = key_b; + + if (a->filter != b->filter) + return FALSE; + + if (a->unique_id_length != b->unique_id_length) + return FALSE; + + return memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0; +} + +static void +_cairo_ps_form_pluck (void *entry, void *closure) +{ + cairo_ps_form_t *surface_entry = entry; + cairo_hash_table_t *patterns = closure; + + _cairo_hash_table_remove (patterns, &surface_entry->base); + free (surface_entry->unique_id); + cairo_surface_destroy (surface_entry->src_surface); + free (surface_entry); +} + +static void _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) { char ctime_buf[26]; @@ -209,7 +312,7 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) _cairo_output_stream_printf (surface->final_stream, "%%!PS-Adobe-3.0%s\n" - "%%%%Creator: cairo %s (http://cairographics.org)\n" + "%%%%Creator: cairo %s (https://cairographics.org)\n" "%%%%CreationDate: %s" "%%%%Pages: %d\n", eps_header, @@ -259,10 +362,10 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) if (!has_bbox) { _cairo_output_stream_printf (surface->final_stream, "%%%%BoundingBox: %d %d %d %d\n", - surface->bbox_x1, - surface->bbox_y1, - surface->bbox_x2, - surface->bbox_y2); + surface->document_bbox_p1.x, + surface->document_bbox_p1.y, + surface->document_bbox_p2.x, + surface->document_bbox_p2.y); } _cairo_output_stream_printf (surface->final_stream, @@ -273,7 +376,6 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) if (surface->eps) { _cairo_output_stream_printf (surface->final_stream, - "save\n" "50 dict begin\n"); } else { _cairo_output_stream_printf (surface->final_stream, @@ -309,9 +411,6 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) "/W* { eoclip } bind def\n" "/BT { } bind def\n" "/ET { } bind def\n" - "/pdfmark where { pop globaldict /?pdfmark /exec load put }\n" - " { globaldict begin /?pdfmark /pop load def /pdfmark\n" - " /cleartomark load def end } ifelse\n" "/BDC { mark 3 1 roll /BDC pdfmark } bind def\n" "/EMC { mark /EMC pdfmark } bind def\n" "/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def\n" @@ -336,6 +435,11 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) "/g { setgray } bind def\n" "/rg { setrgbcolor } bind def\n" "/d1 { setcachedevice } bind def\n" + "/cairo_data_source {\n" + " CairoDataIndex CairoData length lt\n" + " { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def }\n" + " { () } ifelse\n" + "} def\n" "/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def\n" "/cairo_image { image cairo_flush_ascii85_file } def\n" "/cairo_imagemask { imagemask cairo_flush_ascii85_file } def\n"); @@ -374,11 +478,25 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) " } ifelse\n" "} def\n"); } + if (surface->contains_eps) { + _cairo_output_stream_printf (surface->final_stream, + "/cairo_eps_begin {\n" + " /cairo_save_state save def\n" + " /dict_count countdictstack def\n" + " /op_count count 1 sub def\n" + " userdict begin\n" + " /showpage { } def\n" + " 0 g 0 J 1 w 0 j 10 M [ ] 0 d n\n" + "} bind def\n" + "/cairo_eps_end {\n" + " count op_count sub { pop } repeat\n" + " countdictstack dict_count sub { end } repeat\n" + " cairo_save_state restore\n" + "} bind def\n"); + } _cairo_output_stream_printf (surface->final_stream, "%%%%EndProlog\n"); - _cairo_output_stream_printf (surface->final_stream, - "%%%%BeginSetup\n"); num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments); if (num_comments) { @@ -673,7 +791,7 @@ _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->final_stream, "8 dict begin\n" "/FontType 3 def\n" - "/FontMatrix [1 0 0 1 0 0] def\n" + "/FontMatrix [1 0 0 -1 0 0] def\n" "/Encoding 256 array def\n" "0 1 255 { Encoding exch /.notdef put } for\n"); @@ -836,6 +954,16 @@ _cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface) surface); } + +static cairo_int_status_t +_cairo_ps_surface_emit_forms (cairo_ps_surface_t *surface) +{ + _cairo_hash_table_foreach (surface->forms, + _cairo_ps_form_emit, + surface); + return surface->base.status; +} + static cairo_status_t _cairo_ps_surface_emit_body (cairo_ps_surface_t *surface) { @@ -863,7 +991,7 @@ _cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface) if (surface->eps) { _cairo_output_stream_printf (surface->final_stream, - "end restore\n"); + "end\n"); } _cairo_output_stream_printf (surface->final_stream, @@ -882,11 +1010,11 @@ _path_covers_bbox (cairo_ps_surface_t *surface, _cairo_box_round_to_rectangle (&box, &rect); /* skip trivial whole-page clips */ - if (_cairo_rectangle_intersect (&rect, &surface->page_bbox)) { - if (rect.x == surface->page_bbox.x && - rect.width == surface->page_bbox.width && - rect.y == surface->page_bbox.y && - rect.height == surface->page_bbox.height) + if (_cairo_rectangle_intersect (&rect, &surface->surface_extents)) { + if (rect.x == surface->surface_extents.x && + rect.width == surface->surface_extents.width && + rect.y == surface->surface_extents.y && + rect.height == surface->surface_extents.height) { return TRUE; } @@ -975,7 +1103,7 @@ _cairo_ps_surface_get_page_media (cairo_ps_surface_t *surface) } } - page = malloc (sizeof (cairo_page_media_t)); + page = _cairo_malloc (sizeof (cairo_page_media_t)); if (unlikely (page == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; @@ -1011,7 +1139,7 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, cairo_status_t status, status_ignored; cairo_ps_surface_t *surface; - surface = malloc (sizeof (cairo_ps_surface_t)); + surface = _cairo_malloc (sizeof (cairo_ps_surface_t)); if (unlikely (surface == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; @@ -1020,7 +1148,8 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, _cairo_surface_init (&surface->base, &cairo_ps_surface_backend, NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); + CAIRO_CONTENT_COLOR_ALPHA, + TRUE); /* is_vector */ surface->final_stream = stream; @@ -1055,17 +1184,23 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, surface->ps_level_used = CAIRO_PS_LEVEL_2; surface->width = width; surface->height = height; - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, height); + cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, 1, 0, 0); + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); + surface->surface_bounded = TRUE; surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; surface->force_fallbacks = FALSE; surface->content = CAIRO_CONTENT_COLOR_ALPHA; - surface->use_string_datasource = FALSE; surface->current_pattern_is_solid_color = FALSE; - - surface->page_bbox.x = 0; - surface->page_bbox.y = 0; - surface->page_bbox.width = width; - surface->page_bbox.height = height; + surface->document_bbox_p1.x = 0; + surface->document_bbox_p1.y = 0; + surface->document_bbox_p2.x = 0; + surface->document_bbox_p2.y = 0; + surface->total_form_size = 0; + surface->contains_eps = FALSE; + surface->paint_proc = FALSE; _cairo_surface_clipper_init (&surface->clipper, _cairo_ps_surface_clipper_intersect_clip_path); @@ -1081,6 +1216,14 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, _cairo_array_init (&surface->dsc_header_comments, sizeof (char *)); _cairo_array_init (&surface->dsc_setup_comments, sizeof (char *)); _cairo_array_init (&surface->dsc_page_setup_comments, sizeof (char *)); + _cairo_array_init (&surface->recording_surf_stack, sizeof (unsigned int)); + + surface->num_forms = 0; + surface->forms = _cairo_hash_table_create (_cairo_ps_form_equal); + if (unlikely (surface->forms == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_FONT_SUBSETS; + } surface->dsc_comment_target = &surface->dsc_header_comments; @@ -1095,6 +1238,7 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, return surface->paginated_surface; } + CLEANUP_FONT_SUBSETS: _cairo_scaled_font_subsets_destroy (surface->font_subsets); CLEANUP_OUTPUT_STREAM: status_ignored = _cairo_output_stream_destroy (surface->stream); @@ -1403,7 +1547,11 @@ cairo_ps_surface_set_size (cairo_surface_t *surface, ps_surface->width = width_in_points; ps_surface->height = height_in_points; - cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points); + cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, 1, 0, 0); + ps_surface->surface_extents.x = 0; + ps_surface->surface_extents.y = 0; + ps_surface->surface_extents.width = ceil (ps_surface->width); + ps_surface->surface_extents.height = ceil (ps_surface->height); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators, &ps_surface->cairo_to_ps); status = _cairo_paginated_surface_set_size (ps_surface->paginated_surface, @@ -1613,10 +1761,17 @@ _cairo_ps_surface_finish (void *abstract_surface) _cairo_ps_surface_emit_header (surface); + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginSetup\n"); + status = _cairo_ps_surface_emit_font_subsets (surface); if (unlikely (status)) goto CLEANUP; + status = _cairo_ps_surface_emit_forms (surface); + if (unlikely (status)) + goto CLEANUP; + _cairo_output_stream_printf (surface->final_stream, "%%%%EndSetup\n"); @@ -1627,6 +1782,10 @@ _cairo_ps_surface_finish (void *abstract_surface) _cairo_ps_surface_emit_footer (surface); CLEANUP: + _cairo_hash_table_foreach (surface->forms, + _cairo_ps_form_pluck, + surface->forms); + _cairo_hash_table_destroy (surface->forms); _cairo_scaled_font_subsets_destroy (surface->font_subsets); status2 = _cairo_output_stream_destroy (surface->stream); @@ -1668,6 +1827,8 @@ CLEANUP: free (comments[i]); _cairo_array_fini (&surface->dsc_page_setup_comments); + _cairo_array_fini (&surface->recording_surf_stack); + _cairo_surface_clipper_reset (&surface->clipper); return status; @@ -1715,133 +1876,97 @@ color_is_gray (double red, double green, double blue) /** * _cairo_ps_surface_acquire_source_surface_from_pattern: - * @surface: the ps surface - * @pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source - * @extents: extents of the operation that is using this source - * @width: returns width of surface - * @height: returns height of surface - * @x_offset: returns x offset of surface - * @y_offset: returns y offset of surface - * @surface: returns surface of type image surface or recording surface - * @image_extra: returns image extra for image type surface + * @surface: [in] the ps surface + * @pattern: [in] A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use + * as the source + * @extents: [in] extents of the operation that is using this source + * @src_surface_extents: [out] return source surface extents + * @src_surface_bounded: [out] return TRUE if source surface is bounded + * @src_op_extents: [out] return operation extents in source space + * @source_surface: [out] returns surface of type image surface or recording surface + * @x_offset: [out] return x offset of surface + * @y_offset: [out] return y offset of surface * * Acquire source surface or raster source pattern. **/ static cairo_status_t -_cairo_ps_surface_acquire_source_surface_from_pattern (cairo_ps_surface_t *surface, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents, - int *width, - int *height, - double *x_offset, - double *y_offset, - cairo_surface_t **source_surface, - void **image_extra) +_cairo_ps_surface_acquire_source_surface_from_pattern ( + cairo_ps_surface_t *surface, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + cairo_rectangle_int_t *src_surface_extents, + cairo_bool_t *src_surface_bounded, + cairo_rectangle_int_t *src_op_extents, + cairo_surface_t **source_surface, + double *x_offset, + double *y_offset) { - cairo_status_t status; - cairo_image_surface_t *image; + cairo_status_t status; + cairo_box_t bbox; - *x_offset = *y_offset = 0; - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SURFACE: { - cairo_surface_t *surf = ((cairo_surface_pattern_t *) pattern)->surface; + *x_offset = 0; + *y_offset = 0; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&bbox, extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &bbox, NULL); + _cairo_box_round_to_rectangle (&bbox, src_op_extents); + + if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + cairo_surface_t *surf; + + surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, src_op_extents); + if (!surf) + return CAIRO_INT_STATUS_UNSUPPORTED; + *src_surface_bounded = _cairo_surface_get_extents (surf, src_surface_extents); + cairo_surface_get_device_offset (surf, x_offset, y_offset); + *source_surface = surf; + } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_t *surf = NULL; + + *source_surface = ((cairo_surface_pattern_t *) pattern)->surface; + surf = *source_surface; + *src_surface_bounded = _cairo_surface_get_extents (surf, src_surface_extents); if (surf->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (_cairo_surface_is_snapshot (surf)) + surf = _cairo_surface_snapshot_get_target (surf); + if (surf->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surf; - *width = sub->extents.width; - *height = sub->extents.height; - } else { - cairo_surface_t *free_me = NULL; - cairo_recording_surface_t *recording_surface; - cairo_box_t bbox; - cairo_rectangle_int_t extents; - - recording_surface = (cairo_recording_surface_t *) surf; - if (_cairo_surface_is_snapshot (&recording_surface->base)) { - free_me = _cairo_surface_snapshot_get_target (&recording_surface->base); - recording_surface = (cairo_recording_surface_t *) free_me; - } - - status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); - cairo_surface_destroy (free_me); - if (unlikely (status)) - return status; - - _cairo_box_round_to_rectangle (&bbox, &extents); - *width = extents.width; - *height = extents.height; + *src_surface_extents = sub->extents; + *src_surface_bounded = TRUE; + *x_offset = -sub->extents.x; + *y_offset = -sub->extents.y; } - *source_surface = surf; - return CAIRO_STATUS_SUCCESS; - } else { - status = _cairo_surface_acquire_source_image (surf, &image, image_extra); + cairo_surface_destroy (surf); + } else if (surf->type != CAIRO_SURFACE_TYPE_IMAGE) { + cairo_image_surface_t *image; + void *image_extra; + + status = _cairo_surface_acquire_source_image (surf, &image, &image_extra); if (unlikely (status)) return status; - } - } break; - case CAIRO_PATTERN_TYPE_RASTER_SOURCE: { - cairo_surface_t *surf; - cairo_box_t box; - cairo_rectangle_int_t rect; - - /* get the operation extents in pattern space */ - _cairo_box_from_rectangle (&box, extents); - _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL); - _cairo_box_round_to_rectangle (&box, &rect); - surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, &rect); - if (!surf) - return CAIRO_INT_STATUS_UNSUPPORTED; - assert (_cairo_surface_is_image (surf)); - image = (cairo_image_surface_t *) surf; - } break; - - case CAIRO_PATTERN_TYPE_SOLID: - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - case CAIRO_PATTERN_TYPE_MESH: - default: + *src_surface_bounded = _cairo_surface_get_extents (&image->base, src_surface_extents); + _cairo_surface_release_source_image (surf, image, image_extra); + } + } else { ASSERT_NOT_REACHED; - break; } - *width = image->width; - *height = image->height; - *source_surface = &image->base; return CAIRO_STATUS_SUCCESS; } static void _cairo_ps_surface_release_source_surface_from_pattern (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern, - cairo_surface_t *source, - void *image_extra) + cairo_surface_t *source_surface) { - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SURFACE: { - cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern; - if (surf_pat->surface->type != CAIRO_SURFACE_TYPE_RECORDING) { - cairo_image_surface_t *image = (cairo_image_surface_t *) source; - _cairo_surface_release_source_image (surf_pat->surface, image, image_extra); - } - } break; - - case CAIRO_PATTERN_TYPE_RASTER_SOURCE: - _cairo_raster_source_pattern_release (pattern, source); - break; - - case CAIRO_PATTERN_TYPE_SOLID: - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - case CAIRO_PATTERN_TYPE_MESH: - default: - - ASSERT_NOT_REACHED; - break; - } + if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + _cairo_raster_source_pattern_release (pattern, source_surface); } /** @@ -1849,11 +1974,8 @@ _cairo_ps_surface_release_source_surface_from_pattern (cairo_ps_surface_t * @surface: the ps surface * @source: The source image * @extents: extents of the operation that is using this source - * @width: returns width of padded image - * @height: returns height of padded image - * @x_offset: returns x offset of padded image - * @y_offset: returns y offset of padded image * @image: returns the padded image or NULL if padding not required to fill @extents + * @image_extents: returns extents of padded image. These extents in are in source image space. * * Creates a padded image if the source image does not fill the extents. **/ @@ -1862,11 +1984,8 @@ _cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t * cairo_image_surface_t *source, const cairo_matrix_t *source_matrix, const cairo_rectangle_int_t *extents, - int *width, - int *height, - double *x_offset, - double *y_offset, - cairo_image_surface_t **image) + cairo_image_surface_t **image, + cairo_rectangle_int_t *image_extents) { cairo_box_t box; cairo_rectangle_int_t rect; @@ -1888,11 +2007,9 @@ _cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t * _cairo_fixed_integer_floor(box.p2.y) > w || _cairo_fixed_integer_floor(box.p2.y) > h) { - pad_image = - _cairo_image_surface_create_with_pixman_format (NULL, - source->pixman_format, - rect.width, rect.height, - 0); + pad_image = _cairo_image_surface_create_with_content (source->base.content, + rect.width, + rect.height); if (pad_image->status) return pad_image->status; @@ -1905,10 +2022,10 @@ _cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t * NULL); _cairo_pattern_fini (&pad_pattern.base); *image = (cairo_image_surface_t *) pad_image; - *width = rect.width; - *height = rect.height; - *x_offset = rect.x; - *y_offset = rect.y; + image_extents->x = rect.x; + image_extents->y = rect.y; + image_extents->width = rect.width; + image_extents->height = rect.height; } else { *image = NULL; status = CAIRO_STATUS_SUCCESS; @@ -1922,27 +2039,32 @@ _cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { - int width, height; + cairo_rectangle_int_t src_surface_extents; + cairo_bool_t src_surface_bounded; + cairo_rectangle_int_t src_op_extents; + cairo_surface_t *source_surface; double x_offset, y_offset; - cairo_surface_t *source; cairo_image_surface_t *image; void *image_extra; - cairo_int_status_t status; + cairo_int_status_t status; cairo_image_transparency_t transparency; status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, - &width, - &height, + &src_surface_extents, + &src_surface_bounded, + &src_op_extents, + &source_surface, &x_offset, - &y_offset, - &source, - &image_extra); + &y_offset); + if (unlikely (status)) + return status; + + status = _cairo_surface_acquire_source_image (source_surface, &image, &image_extra); if (unlikely (status)) return status; - image = (cairo_image_surface_t *) source; if (image->base.status) return image->base.status; @@ -1969,7 +2091,8 @@ _cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t ASSERT_NOT_REACHED; } - _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra); + _cairo_surface_release_source_image (source_surface, image, image_extra); + _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source_surface); return status; } @@ -2156,14 +2279,9 @@ _cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface, /* The "standard" implementation limit for PostScript string sizes is * 65535 characters (see PostScript Language Reference, Appendix - * B). We go one short of that because we sometimes need two - * characters in a string to represent a single ASCII85 byte, (for the - * escape sequences "\\", "\(", and "\)") and we must not split these - * across two strings. So we'd be in trouble if we went right to the - * limit and one of these escape sequences just happened to land at - * the end. + * B). */ -#define STRING_ARRAY_MAX_STRING_SIZE (65535-1) +#define STRING_ARRAY_MAX_STRING_SIZE 65535 #define STRING_ARRAY_MAX_COLUMN 72 typedef struct _string_array_stream { @@ -2171,64 +2289,62 @@ typedef struct _string_array_stream { cairo_output_stream_t *output; int column; int string_size; + int tuple_count; cairo_bool_t use_strings; } string_array_stream_t; static cairo_status_t -_string_array_stream_write (cairo_output_stream_t *base, - const unsigned char *data, - unsigned int length) +_base85_string_wrap_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) { string_array_stream_t *stream = (string_array_stream_t *) base; unsigned char c; - const unsigned char backslash = '\\'; if (length == 0) return CAIRO_STATUS_SUCCESS; while (length--) { - if (stream->string_size == 0 && stream->use_strings) { - _cairo_output_stream_printf (stream->output, "("); - stream->column++; - } - - c = *data++; - if (stream->use_strings) { - switch (c) { - case '\\': - case '(': - case ')': - _cairo_output_stream_write (stream->output, &backslash, 1); - stream->column++; - stream->string_size++; - break; + if (stream->column == 0) { + if (stream->use_strings) { + _cairo_output_stream_printf (stream->output, "<~"); + stream->column = 2; + } else { + _cairo_output_stream_printf (stream->output, " "); + stream->column = 1; } } - /* Have to be careful to never split the final ~> sequence. */ - if (c == '~') { - _cairo_output_stream_write (stream->output, &c, 1); - stream->column++; - stream->string_size++; - - if (length-- == 0) - break; - c = *data++; - } + c = *data++; _cairo_output_stream_write (stream->output, &c, 1); stream->column++; - stream->string_size++; + /* Base85 encodes each 4 byte tuple with a 5 ASCII character + * tuple, except for 'z' with represents 4 zero bytes. We need + * to keep track of the string length after decoding. + */ + if (c == 'z') { + stream->string_size += 4; + stream->tuple_count = 0; + } else { + if (++stream->tuple_count == 5) { + stream->string_size += 4; + stream->tuple_count = 0; + } + } + + /* Split string at tuple boundary when there is not enough + * space for another tuple */ if (stream->use_strings && - stream->string_size >= STRING_ARRAY_MAX_STRING_SIZE) + stream->tuple_count == 0 && + stream->string_size > STRING_ARRAY_MAX_STRING_SIZE - 4) { - _cairo_output_stream_printf (stream->output, ")\n"); + _cairo_output_stream_printf (stream->output, "~>\n"); stream->string_size = 0; stream->column = 0; } if (stream->column >= STRING_ARRAY_MAX_COLUMN) { _cairo_output_stream_printf (stream->output, "\n "); - stream->string_size += 2; stream->column = 1; } } @@ -2237,78 +2353,75 @@ _string_array_stream_write (cairo_output_stream_t *base, } static cairo_status_t -_string_array_stream_close (cairo_output_stream_t *base) +_base85_string_wrap_stream_close (cairo_output_stream_t *base) { - cairo_status_t status; string_array_stream_t *stream = (string_array_stream_t *) base; - if (stream->use_strings) - _cairo_output_stream_printf (stream->output, ")\n"); - - status = _cairo_output_stream_get_status (stream->output); + if (!stream->use_strings || stream->string_size != 0) + _cairo_output_stream_printf (stream->output, "~>"); - return status; + return _cairo_output_stream_get_status (stream->output); } -/* A string_array_stream wraps an existing output stream. It takes the - * data provided to it and output one or more consecutive string - * objects, each within the standard PostScript implementation limit - * of 65k characters. - * - * The strings are each separated by a space character for easy - * inclusion within an array object, (but the array delimiters are not - * added by the string_array_stream). - * +/* A _base85_strings_stream wraps an existing output stream. It takes + * base85 encoded data and splits it into strings each limited to + * STRING_ARRAY_MAX_STRING_SIZE bytes when decoded. Each string is + * enclosed in "<~" and "~>". + * The string array stream is also careful to wrap the output within - * STRING_ARRAY_MAX_COLUMN columns (+/- 1). The stream also adds - * necessary escaping for special characters within a string, - * (specifically '\', '(', and ')'). + * STRING_ARRAY_MAX_COLUMN columns. Wrapped lines start with a space + * in case an encoded line starts with %% which could be interpreted + * as a DSC comment. */ static cairo_output_stream_t * -_string_array_stream_create (cairo_output_stream_t *output) +_base85_strings_stream_create (cairo_output_stream_t *output) { string_array_stream_t *stream; - stream = malloc (sizeof (string_array_stream_t)); + stream = _cairo_malloc (sizeof (string_array_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, - _string_array_stream_write, + _base85_string_wrap_stream_write, NULL, - _string_array_stream_close); + _base85_string_wrap_stream_close); stream->output = output; stream->column = 0; stream->string_size = 0; + stream->tuple_count = 0; stream->use_strings = TRUE; return &stream->base; } -/* A base85_array_stream wraps an existing output stream. It wraps the - * output within STRING_ARRAY_MAX_COLUMN columns (+/- 1). The output - * is not enclosed in strings like string_array_stream. +/* A base85_wrap_stream wraps an existing output stream. It wraps the + * output within STRING_ARRAY_MAX_COLUMN columns. A base85 EOD "~>" is + * appended to the end. Wrapped lines start with a space in case an + * encoded line starts with %% which could be interpreted as a DSC + * comment. */ static cairo_output_stream_t * -_base85_array_stream_create (cairo_output_stream_t *output) +_base85_wrap_stream_create (cairo_output_stream_t *output) { string_array_stream_t *stream; - stream = malloc (sizeof (string_array_stream_t)); + stream = _cairo_malloc (sizeof (string_array_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, - _string_array_stream_write, + _base85_string_wrap_stream_write, NULL, - _string_array_stream_close); + _base85_string_wrap_stream_close); stream->output = output; stream->column = 0; stream->string_size = 0; + stream->tuple_count = 0; stream->use_strings = FALSE; return &stream->base; @@ -2370,9 +2483,9 @@ _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, cairo_status_t status, status2; if (use_strings) - string_array_stream = _string_array_stream_create (surface->stream); + string_array_stream = _base85_strings_stream_create (surface->stream); else - string_array_stream = _base85_array_stream_create (surface->stream); + string_array_stream = _base85_wrap_stream_create (surface->stream); status = _cairo_output_stream_get_status (string_array_stream); if (unlikely (status)) @@ -2419,9 +2532,6 @@ _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, break; } status = _cairo_output_stream_destroy (base85_stream); - - /* Mark end of base85 data */ - _cairo_output_stream_printf (string_array_stream, "~>"); status2 = _cairo_output_stream_destroy (string_array_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; @@ -2429,12 +2539,32 @@ _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, return status; } +static const char * +get_interpolate (cairo_filter_t filter) +{ + const char *interpolate; + + switch (filter) { + default: + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + interpolate = "true"; + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + interpolate = "false"; + break; + } + + return interpolate; +} + static cairo_status_t -_cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, - cairo_image_surface_t *image_surf, - cairo_operator_t op, - cairo_filter_t filter, - cairo_bool_t stencil_mask) +_cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, + cairo_emit_surface_mode_t mode, + cairo_emit_surface_params_t *params) { cairo_status_t status; unsigned char *data; @@ -2450,10 +2580,16 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, const char *interpolate; cairo_ps_compress_t compress; const char *compress_filter; + cairo_image_surface_t *image_surf; cairo_image_surface_t *image; + void *image_extra; + + if (params->src_surface->status) + return params->src_surface->status; - if (image_surf->base.status) - return image_surf->base.status; + status = _cairo_surface_acquire_source_image (params->src_surface, &image_surf, &image_extra); + if (unlikely (status)) + return status; image = image_surf; if (image->format != CAIRO_FORMAT_RGB24 && @@ -2464,16 +2600,16 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, cairo_surface_t *surf; cairo_surface_pattern_t pattern; - surf = _cairo_image_surface_create_with_content (image_surf->base.content, - image_surf->width, - image_surf->height); + surf = _cairo_image_surface_create_with_content (image->base.content, + image->width, + image->height); image = (cairo_image_surface_t *) surf; if (surf->status) { status = surf->status; goto bail0; } - _cairo_pattern_init_for_surface (&pattern, &image_surf->base); + _cairo_pattern_init_for_surface (&pattern, &image->base); status = _cairo_surface_paint (surf, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); @@ -2482,22 +2618,9 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, goto bail0; } ps_image = image; + interpolate = get_interpolate (params->filter); - switch (filter) { - default: - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - interpolate = "true"; - break; - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - interpolate = "false"; - break; - } - - if (stencil_mask) { + if (params->stencil_mask) { use_mask = FALSE; color = CAIRO_IMAGE_IS_MONOCHROME; transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; @@ -2508,7 +2631,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, current image over a white (or black for CONTENT_COLOR surfaces) RGB surface to eliminate it. */ - if (op == CAIRO_OPERATOR_SOURCE || + if (params->op == CAIRO_OPERATOR_SOURCE || transparency == CAIRO_IMAGE_HAS_ALPHA || (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA && surface->ps_level == CAIRO_PS_LEVEL_2)) @@ -2550,7 +2673,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, if (use_mask) data_size += (ps_image->width + 7)/8; data_size *= ps_image->height; - data = malloc (data_size); + data = _cairo_malloc (data_size); if (unlikely (data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail1; @@ -2558,7 +2681,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, i = 0; for (y = 0; y < ps_image->height; y++) { - if (stencil_mask || use_mask) { + if (params->stencil_mask || use_mask) { /* mask row */ if (ps_image->format == CAIRO_FORMAT_A1) { pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride); @@ -2599,7 +2722,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, i++; } } - if (stencil_mask) + if (params->stencil_mask) continue; /* image row*/ @@ -2664,11 +2787,11 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, surface->ps_level_used = CAIRO_PS_LEVEL_3; } - if (surface->use_string_datasource) { + if (surface->paint_proc) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator later. */ _cairo_output_stream_printf (surface->stream, - "/CairoImageData [\n"); + "/CairoData [\n"); status = _cairo_ps_surface_emit_base85_string (surface, data, @@ -2681,7 +2804,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->stream, "] def\n"); _cairo_output_stream_printf (surface->stream, - "/CairoImageDataIndex 0 def\n"); + "/CairoDataIndex 0 def\n"); } else { _cairo_output_stream_printf (surface->stream, "/cairo_ascii85_file currentfile /ASCII85Decode filter def\n"); @@ -2690,17 +2813,16 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, if (use_mask) { _cairo_output_stream_printf (surface->stream, "%s setcolorspace\n" - "5 dict dup begin\n" - " /ImageType 3 def\n" - " /InterleaveType 2 def\n" - " /DataDict 8 dict def\n" - " DataDict begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /Interpolate %s def\n" - " /BitsPerComponent %d def\n" - " /Decode [ %s ] def\n", + "<<\n" + " /ImageType 3\n" + " /InterleaveType 2\n" + " /DataDict <<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent %d\n" + " /Decode [ %s ]\n", color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray", ps_image->width, ps_image->height, @@ -2708,85 +2830,89 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8, color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1"); - if (surface->use_string_datasource) { + if (surface->paint_proc) { _cairo_output_stream_printf (surface->stream, - " /DataSource {\n" - " CairoImageData CairoImageDataIndex get\n" - " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" - " CairoImageDataIndex CairoImageData length 1 sub gt\n" - " { /CairoImageDataIndex 0 def } if\n" - " } /ASCII85Decode filter /%s filter def\n", + " /DataSource { cairo_data_source } /%s filter\n", compress_filter); } else { _cairo_output_stream_printf (surface->stream, - " /DataSource cairo_ascii85_file /%s filter def\n", + " /DataSource cairo_ascii85_file /%s filter\n", compress_filter); } _cairo_output_stream_printf (surface->stream, - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - " end\n" - " /MaskDict 8 dict def\n" - " MaskDict begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /Interpolate %s def\n" - " /BitsPerComponent 1 def\n" - " /Decode [ 1 0 ] def\n" - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - " end\n" - "end\n" + " /ImageMatrix [ %d 0 0 %d 0 %d ]\n" + " >>\n" + " /MaskDict <<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [ 1 0 ]\n" + " /ImageMatrix [ %d 0 0 %d 0 %d ]\n" + " >>\n" + ">>\n" "image\n", + ps_image->width, + -ps_image->height, ps_image->height, ps_image->width, ps_image->height, interpolate, + ps_image->width, + -ps_image->height, ps_image->height); } else { - if (!stencil_mask) { + const char *decode; + + if (!params->stencil_mask) { _cairo_output_stream_printf (surface->stream, "%s setcolorspace\n", color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray"); } + if (params->stencil_mask) + decode = "1 0"; + else if (color == CAIRO_IMAGE_IS_COLOR) + decode = "0 1 0 1 0 1"; + else + decode ="0 1"; + _cairo_output_stream_printf (surface->stream, - "8 dict dup begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /Interpolate %s def\n" - " /BitsPerComponent %d def\n" - " /Decode [ %s ] def\n", + "<<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent %d\n" + " /Decode [ %s ]\n", ps_image->width, ps_image->height, interpolate, color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8, - stencil_mask ? "1 0" : color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1"); - if (surface->use_string_datasource) { + decode); + if (surface->paint_proc) { _cairo_output_stream_printf (surface->stream, - " /DataSource {\n" - " CairoImageData CairoImageDataIndex get\n" - " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" - " CairoImageDataIndex CairoImageData length 1 sub gt\n" - " { /CairoImageDataIndex 0 def } if\n" - " } /ASCII85Decode filter /%s filter def\n", + " /DataSource { cairo_data_source } /%s filter\n", compress_filter); } else { _cairo_output_stream_printf (surface->stream, - " /DataSource cairo_ascii85_file /%s filter def\n", + " /DataSource cairo_ascii85_file /%s filter\n", compress_filter); } _cairo_output_stream_printf (surface->stream, - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - "end\n" + " /ImageMatrix [ %d 0 0 %d 0 %d ]\n" + ">>\n" "%s%s\n", + ps_image->width, + -ps_image->height, ps_image->height, - surface->use_string_datasource ? "" : "cairo_", - stencil_mask ? "imagemask" : "image"); + surface->paint_proc ? "" : "cairo_", + params->stencil_mask ? "imagemask" : "image"); } - if (!surface->use_string_datasource) { + if (!surface->paint_proc) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator. */ status = _cairo_ps_surface_emit_base85_string (surface, @@ -2810,14 +2936,15 @@ bail0: if (image != image_surf) cairo_surface_destroy (&image->base); + _cairo_surface_release_source_image (params->src_surface, image_surf, image_extra); + return status; } -static cairo_status_t -_cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, - cairo_surface_t *source, - int width, - int height) +static cairo_int_status_t +_cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, + cairo_emit_surface_mode_t mode, + cairo_emit_surface_params_t *params) { cairo_status_t status; const unsigned char *mime_data; @@ -2826,10 +2953,11 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, const char *colorspace; const char *decode; - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + if (unlikely (params->src_surface->status)) + return params->src_surface->status; + + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); - if (unlikely (source->status)) - return source->status; if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -2854,11 +2982,18 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - if (surface->use_string_datasource) { + /* At this point we know emitting jpeg will succeed. */ + if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { + params->is_image = TRUE; + params->approx_size = mime_data_length; + return CAIRO_STATUS_SUCCESS; + } + + if (surface->paint_proc) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator later. */ _cairo_output_stream_printf (surface->stream, - "/CairoImageData [\n"); + "/CairoData [\n"); status = _cairo_ps_surface_emit_base85_string (surface, mime_data, @@ -2871,7 +3006,7 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->stream, "] def\n"); _cairo_output_stream_printf (surface->stream, - "/CairoImageDataIndex 0 def\n"); + "/CairoDataIndex 0 def\n"); } else { _cairo_output_stream_printf (surface->stream, "/cairo_ascii85_file currentfile /ASCII85Decode filter def\n"); @@ -2879,39 +3014,38 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->stream, "%s setcolorspace\n" - "8 dict dup begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /BitsPerComponent %d def\n" - " /Decode [ %s ] def\n", + "<<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent %d\n" + " /Interpolate %s\n" + " /Decode [ %s ]\n", colorspace, info.width, info.height, info.bits_per_component, + get_interpolate (params->filter), decode); - if (surface->use_string_datasource) { + if (surface->paint_proc) { _cairo_output_stream_printf (surface->stream, - " /DataSource {\n" - " CairoImageData CairoImageDataIndex get\n" - " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" - " CairoImageDataIndex CairoImageData length 1 sub gt\n" - " { /CairoImageDataIndex 0 def } if\n" - " } /ASCII85Decode filter /DCTDecode filter def\n"); + " /DataSource { cairo_data_source } /DCTDecode filter\n"); } else { _cairo_output_stream_printf (surface->stream, - " /DataSource cairo_ascii85_file /DCTDecode filter def\n"); + " /DataSource cairo_ascii85_file /DCTDecode filter\n"); } _cairo_output_stream_printf (surface->stream, - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - "end\n" + " /ImageMatrix [ %d 0 0 %d 0 %d ]\n" + ">>\n" "%simage\n", + info.width, + -info.height, info.height, - surface->use_string_datasource ? "" : "cairo_"); + surface->paint_proc ? "" : "cairo_"); - if (!surface->use_string_datasource) { + if (!surface->paint_proc) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator. */ status = _cairo_ps_surface_emit_base85_string (surface, @@ -2924,117 +3058,309 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, return status; } -static cairo_status_t -_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, - cairo_surface_t *recording_surface) +static cairo_int_status_t +_cairo_ps_surface_emit_ccitt_image (cairo_ps_surface_t *surface, + cairo_emit_surface_mode_t mode, + cairo_emit_surface_params_t *params) { - double old_width, old_height; - cairo_matrix_t old_cairo_to_ps; - cairo_content_t old_content; - cairo_rectangle_int_t old_page_bbox; - cairo_surface_t *free_me = NULL; - cairo_surface_clipper_t old_clipper; - cairo_box_t bbox; - cairo_int_status_t status; + cairo_status_t status; + const unsigned char *ccitt_data; + unsigned long ccitt_data_len; + const unsigned char *ccitt_params_data; + unsigned long ccitt_params_data_len; + char *ccitt_params_string; + cairo_ccitt_params_t ccitt_params; + + if (unlikely (params->src_surface->status)) + return params->src_surface->status; + + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_CCITT_FAX, + &ccitt_data, &ccitt_data_len); + if (ccitt_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; - old_content = surface->content; - old_width = surface->width; - old_height = surface->height; - old_page_bbox = surface->page_bbox; - old_cairo_to_ps = surface->cairo_to_ps; - old_clipper = surface->clipper; - _cairo_surface_clipper_init (&surface->clipper, - _cairo_ps_surface_clipper_intersect_clip_path); + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + &ccitt_params_data, &ccitt_params_data_len); + if (ccitt_params_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; - if (_cairo_surface_is_snapshot (recording_surface)) - free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface); + /* ensure params_string is null terminated */ + ccitt_params_string = malloc (ccitt_params_data_len + 1); + memcpy (ccitt_params_string, ccitt_params_data, ccitt_params_data_len); + ccitt_params_string[ccitt_params_data_len] = 0; + status = _cairo_tag_parse_ccitt_params (ccitt_params_string, &ccitt_params); + if (unlikely(status)) + return status; - status = - _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, - &bbox, - NULL); - if (unlikely (status)) - goto err; + free (ccitt_params_string); -#if DEBUG_PS - _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_emit_recording_surface (%f, %f), (%f, %f)\n", - _cairo_fixed_to_double (bbox.p1.x), - _cairo_fixed_to_double (bbox.p1.y), - _cairo_fixed_to_double (bbox.p2.x), - _cairo_fixed_to_double (bbox.p2.y)); -#endif + if (ccitt_params.columns <= 0 || ccitt_params.rows <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; - surface->width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); - surface->height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); - _cairo_box_round_to_rectangle (&bbox, &surface->page_bbox); + /* At this point we know emitting ccitt will succeed. */ + if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { + params->is_image = TRUE; + params->approx_size = ccitt_data_len; + return CAIRO_STATUS_SUCCESS; + } - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_ps); - _cairo_output_stream_printf (surface->stream, " q\n"); + if (surface->paint_proc) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator later. */ + _cairo_output_stream_printf (surface->stream, + "/CairoData [\n"); + + status = _cairo_ps_surface_emit_base85_string (surface, + ccitt_data, + ccitt_data_len, + CAIRO_PS_COMPRESS_NONE, + TRUE); + if (unlikely (status)) + return status; - if (recording_surface->content == CAIRO_CONTENT_COLOR) { - surface->content = CAIRO_CONTENT_COLOR; _cairo_output_stream_printf (surface->stream, - " 0 g %d %d %d %d rectfill\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); + "] def\n"); + _cairo_output_stream_printf (surface->stream, + "/CairoDataIndex 0 def\n"); + } else { + _cairo_output_stream_printf (surface->stream, + "/cairo_ascii85_file currentfile /ASCII85Decode filter def\n"); } - status = _cairo_recording_surface_replay_region (recording_surface, - NULL, - &surface->base, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) - goto err; + if (!params->stencil_mask) { + _cairo_output_stream_printf (surface->stream, + "/DeviceGray setcolorspace\n"); + } - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - goto err; + _cairo_output_stream_printf (surface->stream, + "<<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 1\n" + " /Interpolate %s\n" + " /Decode [ 0 1 ]\n", + ccitt_params.columns, + ccitt_params.rows, + get_interpolate (params->filter)); + + if (surface->paint_proc) { + _cairo_output_stream_printf (surface->stream, + " /DataSource { cairo_data_source }\n"); + } else { + _cairo_output_stream_printf (surface->stream, + " /DataSource cairo_ascii85_file\n"); + } - _cairo_output_stream_printf (surface->stream, " Q\n"); + _cairo_output_stream_printf (surface->stream, + " << /Columns %d /Rows %d /K %d\n", + ccitt_params.columns, + ccitt_params.rows, + ccitt_params.k); - _cairo_surface_clipper_reset (&surface->clipper); - surface->clipper = old_clipper; - surface->content = old_content; - surface->width = old_width; - surface->height = old_height; - surface->page_bbox = old_page_bbox; - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - surface->cairo_to_ps = old_cairo_to_ps; + if (ccitt_params.end_of_line) + _cairo_output_stream_printf (surface->stream, " /EndOfLine true\n"); - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_ps); + if (ccitt_params.encoded_byte_align) + _cairo_output_stream_printf (surface->stream, " /EncodedByteAlign true\n"); + + if (!ccitt_params.end_of_block) + _cairo_output_stream_printf (surface->stream, " /EndOfBlock false\n"); + + if (ccitt_params.black_is_1) + _cairo_output_stream_printf (surface->stream, " /BlackIs1 true\n"); + + if (ccitt_params.damaged_rows_before_error > 0) { + _cairo_output_stream_printf (surface->stream, + " /DamagedRowsBeforeError %d\n", + ccitt_params.damaged_rows_before_error); + } + + _cairo_output_stream_printf (surface->stream, + " >> /CCITTFaxDecode filter\n"); + + _cairo_output_stream_printf (surface->stream, + " /ImageMatrix [ %d 0 0 %d 0 %d ]\n" + ">>\n" + "%s%s\n", + ccitt_params.columns, + -ccitt_params.rows, + ccitt_params.rows, + surface->paint_proc ? "" : "cairo_", + params->stencil_mask ? "imagemask" : "image"); + + if (!surface->paint_proc) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator. */ + status = _cairo_ps_surface_emit_base85_string (surface, + ccitt_data, + ccitt_data_len, + CAIRO_PS_COMPRESS_NONE, + FALSE); + } -err: - cairo_surface_destroy (free_me); return status; } -static cairo_int_status_t -_cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface, - cairo_surface_t *recording_surface, - const cairo_rectangle_int_t *extents) +/* The '|' character is not used in PS (including ASCII85). We can + * speed up the search by first searching for the first char before + * comparing strings. + */ +#define SUBFILE_FILTER_EOD "|EOD|" + +/* Count number of non overlapping occurrences of SUBFILE_FILTER_EOD in data. */ +static int +count_eod_strings (const unsigned char *data, unsigned long data_len) +{ + const unsigned char *p = data; + const unsigned char *end; + int first_char, len, count; + const char *eod_str = SUBFILE_FILTER_EOD; + + first_char = eod_str[0]; + len = strlen (eod_str); + p = data; + end = data + data_len - len + 1; + count = 0; + while (p < end) { + p = memchr (p, first_char, end - p); + if (!p) + break; + + if (memcmp (p, eod_str, len) == 0) { + count++; + p += len; + } + } + + return count; +} + +static cairo_status_t +_cairo_ps_surface_emit_eps (cairo_ps_surface_t *surface, + cairo_emit_surface_mode_t mode, + cairo_emit_surface_params_t *params) +{ + cairo_status_t status; + const unsigned char *eps_data = NULL; + unsigned long eps_data_len; + const unsigned char *eps_params_string = NULL; + unsigned long eps_params_string_len; + char *params_string = NULL; + cairo_eps_params_t eps_params; + cairo_matrix_t mat; + double eps_width, eps_height; + + if (unlikely (params->src_surface->status)) + return params->src_surface->status; + + /* We only embed EPS with level 3 as we may use ReusableStreamDecode and we + * don't know what level the EPS file requires. */ + if (surface->ps_level == CAIRO_PS_LEVEL_2) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_EPS, + &eps_data, &eps_data_len); + if (eps_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_EPS_PARAMS, + &eps_params_string, &eps_params_string_len); + if (eps_params_string == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* ensure params_string is null terminated */ + params_string = malloc (eps_params_string_len + 1); + memcpy (params_string, eps_params_string, eps_params_string_len); + params_string[eps_params_string_len] = 0; + status = _cairo_tag_parse_eps_params (params_string, &eps_params); + if (unlikely(status)) + return status; + + /* At this point we know emitting EPS will succeed. */ + if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { + params->is_image = FALSE; + params->approx_size = eps_data_len; + surface->contains_eps = TRUE; + + /* Find number of occurrences of SUBFILE_FILTER_EOD in the EPS data. + * We will need it before emitting the data if a ReusableStream is used. + */ + params->eod_count = count_eod_strings (eps_data, eps_data_len); + return CAIRO_STATUS_SUCCESS; + } + + surface->ps_level_used = CAIRO_PS_LEVEL_3; + _cairo_output_stream_printf (surface->stream, "cairo_eps_begin\n"); + + eps_width = eps_params.bbox.p2.x - eps_params.bbox.p1.x; + eps_height = eps_params.bbox.p2.y - eps_params.bbox.p1.y; + cairo_matrix_init_translate (&mat, + params->src_surface_extents->x, + params->src_surface_extents->y); + cairo_matrix_scale (&mat, + params->src_surface_extents->width/eps_width, + params->src_surface_extents->height/eps_height); + cairo_matrix_scale (&mat, 1, -1); + cairo_matrix_translate (&mat, -eps_params.bbox.p1.x, -eps_params.bbox.p2.y); + + if (! _cairo_matrix_is_identity (&mat)) { + _cairo_output_stream_printf (surface->stream, "[ "); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, " ] concat\n"); + } + + _cairo_output_stream_printf (surface->stream, + "%f %f %f %f rectclip\n", + eps_params.bbox.p1.x, + eps_params.bbox.p1.y, + eps_width, + eps_height); + + _cairo_output_stream_write (surface->stream, eps_data, eps_data_len); + _cairo_output_stream_printf (surface->stream, "\ncairo_eps_end\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, + cairo_surface_t *recording_surface, + const cairo_rectangle_int_t *recording_extents, + cairo_bool_t subsurface) { double old_width, old_height; + cairo_rectangle_int_t old_surface_extents; + cairo_bool_t old_surface_bounded; cairo_matrix_t old_cairo_to_ps; cairo_content_t old_content; - cairo_rectangle_int_t old_page_bbox; cairo_surface_clipper_t old_clipper; - cairo_surface_t *free_me = NULL; cairo_int_status_t status; + cairo_surface_t *free_me = NULL; + unsigned int id; + int i, recording_surf_stack_size; + + /* Prevent infinite recursion if the recording_surface references a recording + * currently being emitted */ + recording_surf_stack_size = _cairo_array_num_elements (&surface->recording_surf_stack); + for (i = 0; i < recording_surf_stack_size; i++) { + _cairo_array_copy_element (&surface->recording_surf_stack, i, &id); + if (id == recording_surface->unique_id) + return CAIRO_STATUS_SUCCESS; + } + id = recording_surface->unique_id; + status = _cairo_array_append (&surface->recording_surf_stack, &id); + if (unlikely (status)) + return status; + + if (_cairo_surface_is_snapshot (recording_surface)) + free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface); old_content = surface->content; old_width = surface->width; old_height = surface->height; - old_page_bbox = surface->page_bbox; + old_surface_extents = surface->surface_extents; + old_surface_bounded = surface->surface_bounded; old_cairo_to_ps = surface->cairo_to_ps; old_clipper = surface->clipper; _cairo_surface_clipper_init (&surface->clipper, @@ -3042,46 +3368,44 @@ _cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface, #if DEBUG_PS _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_emit_recording_subsurface (%d, %d), (%d, %d)\n", - extents->x, extents->y, - extents->width, extents->height); + "%% _cairo_ps_surface_emit_recording_surface" + " x: %d, y: %d, w: %d, h: %d subsurface: %d\n", + recording_extents->x, recording_extents->y, + recording_extents->width, recording_extents->height, + subsurface); #endif - surface->page_bbox.x = surface->page_bbox.y = 0; - surface->page_bbox.width = surface->width = extents->width; - surface->page_bbox.height = surface->height = extents->height; - + surface->width = recording_extents->width; + surface->height = recording_extents->height; + surface->surface_extents = *recording_extents; surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); + cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, 1, 0, 0); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); _cairo_output_stream_printf (surface->stream, " q\n"); - if (_cairo_surface_is_snapshot (recording_surface)) - free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface); - if (recording_surface->content == CAIRO_CONTENT_COLOR) { surface->content = CAIRO_CONTENT_COLOR; _cairo_output_stream_printf (surface->stream, " 0 g %d %d %d %d rectfill\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); + recording_extents->x, + recording_extents->y, + recording_extents->width, + recording_extents->height); } status = _cairo_recording_surface_replay_region (recording_surface, - extents, + subsurface ? recording_extents : NULL, &surface->base, CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); if (unlikely (status)) - goto err; + return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - goto err; + return status; _cairo_output_stream_printf (surface->stream, " Q\n"); @@ -3090,16 +3414,18 @@ _cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface, surface->content = old_content; surface->width = old_width; surface->height = old_height; - surface->page_bbox = old_page_bbox; + surface->surface_extents = old_surface_extents; + surface->surface_bounded = old_surface_bounded; surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); surface->cairo_to_ps = old_cairo_to_ps; _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); - -err: cairo_surface_destroy (free_me); + + _cairo_array_truncate (&surface->recording_surf_stack, recording_surf_stack_size); + return status; } @@ -3145,44 +3471,351 @@ _cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface, red, green, blue); } -static cairo_status_t -_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, - cairo_pattern_t *source_pattern, - cairo_surface_t *source_surface, - cairo_operator_t op, - int width, - int height, - cairo_bool_t stencil_mask) +/* + * PS Forms are used for sources that have CAIRO_MIME_TYPE_UNIQUE_ID. They will be + * emitted once in the PS header and can be rendered with the 'execform' operator. + * + * This function tries adding the source the form hash table. If the source does not + * have CAIRO_MIME_TYPE_UNIQUE_ID, CAIRO_INT_STATUS_UNSUPPORTED is returned. + + * @source: [in] the source for the form + * @params: [in] source parameters + * @test: [in] if TRUE, test if form will be used (excludes size check) + * @ps_form [out] the new or exisiting entry int the hash table. + * image or recording. + */ +static cairo_int_status_t +_cairo_ps_surface_use_form (cairo_ps_surface_t *surface, + cairo_emit_surface_params_t *params, + cairo_bool_t test, + cairo_ps_form_t **ps_form) +{ + cairo_ps_form_t source_key; + cairo_ps_form_t *source_entry; + unsigned char *unique_id = NULL; + unsigned long unique_id_length = 0; + cairo_status_t status; + long max_size; + + if (params->op != CAIRO_OPERATOR_OVER || params->stencil_mask) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (params->src_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_UNIQUE_ID, + (const unsigned char **) &source_key.unique_id, + &source_key.unique_id_length); + if (source_key.unique_id == NULL || source_key.unique_id_length == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (test) + return CAIRO_STATUS_SUCCESS; + + source_key.filter = params->filter; + _cairo_ps_form_init_key (&source_key); + source_entry = _cairo_hash_table_lookup (surface->forms, &source_key.base); + if (source_entry) { + _cairo_rectangle_union (&source_entry->required_extents, params->src_op_extents); + *ps_form = source_entry; + return CAIRO_STATUS_SUCCESS; + } + + if (surface->ps_level == CAIRO_PS_LEVEL_3) + max_size = MAX_L3_FORM_DATA; + else + max_size = MAX_L3_FORM_DATA; + + /* Don't add any more Forms if we exceed the form memory limit */ + if (surface->total_form_size + params->approx_size > max_size) + return CAIRO_INT_STATUS_UNSUPPORTED; + + surface->total_form_size += params->approx_size > max_size; + unique_id = _cairo_malloc (source_key.unique_id_length); + if (unique_id == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + unique_id_length = source_key.unique_id_length; + memcpy (unique_id, source_key.unique_id, unique_id_length); + + source_entry = calloc (sizeof (cairo_ps_form_t), 1); + if (source_entry == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + source_entry->unique_id_length = unique_id_length; + source_entry->unique_id = unique_id; + source_entry->id = surface->num_forms++; + source_entry->src_surface = cairo_surface_reference (params->src_surface); + source_entry->required_extents = *params->src_op_extents; + source_entry->filter = params->filter; + source_entry->is_image = params->is_image; + _cairo_ps_form_init_key (source_entry); + status = _cairo_hash_table_insert (surface->forms, &source_entry->base); + if (unlikely(status)) + goto fail; + + *ps_form = source_entry; + return CAIRO_STATUS_SUCCESS; + + fail: + free (unique_id); + free (source_entry); + return status; +} + +static cairo_int_status_t +_cairo_ps_surface_emit_form (cairo_ps_surface_t *surface, + cairo_emit_surface_params_t *params, + cairo_bool_t test) +{ + cairo_ps_form_t *ps_form = NULL; + cairo_status_t status; + + status = _cairo_ps_surface_use_form (surface, + params, + test, + &ps_form); + if (test || status) + return status; + + /* _cairo_ps_form_emit will use Level 3 if permitted by ps_level */ + if (surface->ps_level == CAIRO_PS_LEVEL_3) + surface->ps_level_used = CAIRO_PS_LEVEL_3; + + _cairo_output_stream_printf (surface->stream, + "/cairoform-%d /Form findresource execform\n", + ps_form->id); + + return CAIRO_STATUS_SUCCESS; +} + +/* Emit a surface. This function has three modes. + * + * CAIRO_EMIT_SURFACE_ANALYZE: This will determine the surface type to + * be emitted and approximate size. is_image is set to TRUE if the + * emitted surface is an image surface (including mime images). This + * is used by the caller to setup the correct CTM. approx_size is set + * to the approximate size of the emitted surface and is used as an + * input by the emit mode. + * + * CAIRO_EMIT_SURFACE_EMIT: Emits the surface will be emitted. The + * approx_size and the surface unique id values are used to determine + * if a Form should be used. If a form is used, the exec form + * operation is emitted and the surface is added to the forms hash + * table. + * + * CAIRO_EMIT_SURFACE_EMIT_FORM: Emits the form definition for the surface. + * + * Usage is: + * 1) Setup input params and call with ANALYZE. + * 2) Setup CTM for surface and call with EMIT using same params struct. + * The EMIT_FORM mode is used when emitting the form definitions. + */ +static cairo_int_status_t +_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, + cairo_emit_surface_mode_t mode, + cairo_emit_surface_params_t *params) { cairo_int_status_t status; + cairo_output_stream_t *old_stream = NULL; + cairo_bool_t use_form; + + /* Try emitting as a form. Returns unsupported if the surface is + * deemed unsuitable for a form. */ + use_form = FALSE; + if (mode == CAIRO_EMIT_SURFACE_ANALYZE || mode == CAIRO_EMIT_SURFACE_EMIT) { + status = _cairo_ps_surface_emit_form (surface, + params, + mode == CAIRO_EMIT_SURFACE_ANALYZE); + use_form = (status == CAIRO_INT_STATUS_SUCCESS); + if (status != CAIRO_INT_STATUS_SUCCESS && status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - if (source_pattern->type == CAIRO_PATTERN_TYPE_SURFACE && - source_pattern->extend != CAIRO_EXTEND_PAD) - { - cairo_surface_t *surf = ((cairo_surface_pattern_t *) source_pattern)->surface; + if (mode == CAIRO_EMIT_SURFACE_EMIT && status == CAIRO_INT_STATUS_SUCCESS) + return status; + } + + status = _cairo_ps_surface_emit_eps (surface, mode, params); + if (status == CAIRO_INT_STATUS_SUCCESS) { + params->is_image = FALSE; + goto surface_emitted; + } + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_ps_surface_emit_jpeg_image (surface, mode, params); + if (status == CAIRO_INT_STATUS_SUCCESS) { + params->is_image = TRUE; + goto surface_emitted; + } + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - status = _cairo_ps_surface_emit_jpeg_image (surface, surf, width, height); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) + status = _cairo_ps_surface_emit_ccitt_image (surface, mode, params); + if (status == CAIRO_INT_STATUS_SUCCESS) { + params->is_image = TRUE; + goto surface_emitted; + } + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { + /* Find size of image or recording surface by emitting to a memory stream */ + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) return status; + + old_stream = surface->stream; + surface->stream = _cairo_memory_stream_create (); + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->stream); } - if (source_surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (source_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source_surface; - status = _cairo_ps_surface_emit_recording_subsurface (surface, sub->target, &sub->extents); + if (params->src_surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + params->is_image = FALSE; + if (params->src_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) params->src_surface; + status = _cairo_ps_surface_emit_recording_surface (surface, + sub->target, + &sub->extents, + TRUE); } else { - status = _cairo_ps_surface_emit_recording_surface (surface, source_surface); + status = _cairo_ps_surface_emit_recording_surface (surface, + params->src_surface, + params->src_op_extents, + FALSE); } } else { - cairo_image_surface_t *image = (cairo_image_surface_t *) source_surface; + params->is_image = TRUE; + status = _cairo_ps_surface_emit_image (surface, mode, params); + } + + if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { + unsigned char *data; + unsigned long length; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_memory_stream_destroy (surface->stream, &data, &length); + free (data); + if (unlikely (status)) + return status; - status = _cairo_ps_surface_emit_image (surface, image, - op, source_pattern->filter, stencil_mask); + params->approx_size = length; + surface->stream = old_stream; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, + surface->stream); } + surface_emitted: + return status; } +static void +_cairo_ps_form_emit (void *entry, void *closure) +{ + cairo_ps_form_t *form = entry; + cairo_ps_surface_t *surface = closure; + cairo_emit_surface_params_t params; + cairo_int_status_t status; + cairo_output_stream_t *old_stream; + + params.src_surface = form->src_surface; + params.op = CAIRO_OPERATOR_OVER; + params.src_op_extents = &form->required_extents; + params.filter = form->filter; + params.stencil_mask = FALSE; + params.is_image = form->is_image; + params.approx_size = 0; + + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginResource: form cairoform-%d\n", + form->id); + + _cairo_output_stream_printf (surface->final_stream, + "/cairo_paint_form-%d", + form->id); + if (surface->ps_level == CAIRO_PS_LEVEL_3) { + surface->paint_proc = FALSE; + _cairo_output_stream_printf (surface->final_stream, + "\n" + "currentfile\n" + "<< /Filter /SubFileDecode\n" + " /DecodeParms << /EODString (%s) /EODCount 0 >>\n" + ">> /ReusableStreamDecode filter\n", + SUBFILE_FILTER_EOD); + } else { + surface->paint_proc = TRUE; + _cairo_output_stream_printf (surface->final_stream, + " {\n"); + } + _cairo_output_stream_printf (surface->final_stream, + "5 dict begin\n"); + + old_stream = surface->stream; + surface->stream = surface->final_stream; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->stream); + status = _cairo_ps_surface_emit_surface (surface, + CAIRO_EMIT_SURFACE_EMIT_FORM, + ¶ms); + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + surface->stream = old_stream; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->stream); + + _cairo_output_stream_printf (surface->final_stream, + "end\n"); + if (surface->ps_level == CAIRO_PS_LEVEL_3) { + _cairo_output_stream_printf (surface->final_stream, + "%s\n" + "def\n", + SUBFILE_FILTER_EOD); + } else { + _cairo_output_stream_printf (surface->final_stream, + "} bind def\n"); + } + + _cairo_output_stream_printf (surface->final_stream, + "\n" + "/cairoform-%d\n" + "<<\n" + " /FormType 1\n", + form->id); + + if (form->is_image) { + _cairo_output_stream_printf (surface->final_stream, + " /BBox [ 0 0 1 1 ]\n"); + } else { + _cairo_output_stream_printf (surface->final_stream, + " /BBox [ %d %d %d %d ]\n", + form->required_extents.x, + form->required_extents.y, + form->required_extents.x + form->required_extents.width, + form->required_extents.y + form->required_extents.height); + } + + _cairo_output_stream_printf (surface->final_stream, + " /Matrix [ 1 0 0 1 0 0 ]\n" + " /PaintProc { pop cairo_paint_form-%d", + form->id); + + if (surface->ps_level == CAIRO_PS_LEVEL_3) { + _cairo_output_stream_printf (surface->final_stream, + " dup 0 setfileposition cvx exec"); + } + _cairo_output_stream_printf (surface->final_stream, + " } bind\n" + ">>\n" + "/Form defineresource pop\n"); + + _cairo_output_stream_printf (surface->final_stream, + "%%%%EndResource\n"); + if (status) + surface->base.status = status; +} static void _path_fixed_init_rectangle (cairo_path_fixed_t *path, @@ -3215,19 +3848,21 @@ _path_fixed_init_rectangle (cairo_path_fixed_t *path, static cairo_status_t _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents, cairo_operator_t op, cairo_bool_t stencil_mask) { + cairo_rectangle_int_t src_surface_extents; + cairo_bool_t src_surface_bounded; + cairo_rectangle_int_t src_op_extents; + cairo_surface_t *source_surface; + double x_offset, y_offset; cairo_status_t status; - int width, height; cairo_matrix_t cairo_p2d, ps_p2d; cairo_path_fixed_t path; - double x_offset, y_offset; - cairo_surface_t *source; + cairo_emit_surface_params_t params; cairo_image_surface_t *image = NULL; - void *image_extra; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) @@ -3236,10 +3871,12 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, - &width, &height, - &x_offset, &y_offset, - &source, - &image_extra); + &src_surface_extents, + &src_surface_bounded, + &src_op_extents, + &source_surface, + &x_offset, + &y_offset); if (unlikely (status)) return status; @@ -3248,16 +3885,18 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, ((cairo_surface_pattern_t *)pattern)->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { cairo_image_surface_t *img; - img = (cairo_image_surface_t *) source; + img = (cairo_image_surface_t *) source_surface; status = _cairo_ps_surface_create_padded_image_from_image (surface, img, &pattern->matrix, extents, - &width, &height, - &x_offset, &y_offset, - &image); + &image, + &src_surface_extents); if (unlikely (status)) goto release_source; + + x_offset = src_surface_extents.x; + y_offset = src_surface_extents.y; } _path_fixed_init_rectangle (&path, extents); @@ -3278,8 +3917,8 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, "%% Fallback Image: x=%f y=%f w=%d h=%d ", -cairo_p2d.x0/x_scale, -cairo_p2d.y0/y_scale, - (int)(width/x_scale), - (int)(height/y_scale)); + (int)(src_surface_extents.width/x_scale), + (int)(src_surface_extents.height/y_scale)); if (x_scale == y_scale) { _cairo_output_stream_printf (surface->stream, "res=%fppi ", @@ -3292,14 +3931,16 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, } _cairo_output_stream_printf (surface->stream, "size=%ld\n", - (long)width*height*3); + (long)src_surface_extents.width * src_surface_extents.height * 3); } else { if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, - "%d g 0 0 %f %f rectfill\n", + "%d g %d %d %d %d rectfill\n", surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, - surface->width, - surface->height); + surface->surface_extents.x, + surface->surface_extents.y, + surface->surface_extents.width, + surface->surface_extents.height); } } @@ -3310,8 +3951,26 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, ps_p2d = surface->cairo_to_ps; cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); cairo_matrix_translate (&ps_p2d, x_offset, y_offset); - cairo_matrix_translate (&ps_p2d, 0.0, height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + + params.src_surface = image ? &image->base : source_surface; + params.op = op; + params.src_surface_extents = &src_surface_extents; + params.src_surface_bounded = src_surface_bounded; + params.src_op_extents = &src_op_extents; + params.filter = pattern->filter; + params.stencil_mask = stencil_mask; + params.is_image = FALSE; + params.approx_size = 0; + + status = _cairo_ps_surface_emit_surface (surface, CAIRO_EMIT_SURFACE_ANALYZE, ¶ms); + if (unlikely (status)) + goto release_source; + + if (params.is_image) { + cairo_matrix_translate (&ps_p2d, 0.0, src_surface_extents.height); + cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + cairo_matrix_scale (&ps_p2d, src_surface_extents.width, src_surface_extents.height); + } if (! _cairo_matrix_is_identity (&ps_p2d)) { _cairo_output_stream_printf (surface->stream, "[ "); @@ -3319,18 +3978,13 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->stream, " ] concat\n"); } - status = _cairo_ps_surface_emit_surface (surface, - pattern, - image ? &image->base : source, - op, - width, height, - stencil_mask); + status = _cairo_ps_surface_emit_surface (surface, CAIRO_EMIT_SURFACE_EMIT, ¶ms); release_source: if (image) cairo_surface_destroy (&image->base); - _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra); + _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source_surface); return status; } @@ -3342,15 +3996,17 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, cairo_operator_t op) { cairo_status_t status; - int pattern_width = 0; /* squelch bogus compiler warning */ - int pattern_height = 0; /* squelch bogus compiler warning */ double xstep, ystep; + cairo_rectangle_int_t pattern_extents; + cairo_bool_t bounded; cairo_matrix_t cairo_p2d, ps_p2d; - cairo_bool_t old_use_string_datasource; + cairo_bool_t old_paint_proc; double x_offset, y_offset; - cairo_surface_t *source; + cairo_surface_t *source_surface; cairo_image_surface_t *image = NULL; - void *image_extra; + cairo_rectangle_int_t src_op_extents; + cairo_emit_surface_params_t params; + cairo_extend_t extend = cairo_pattern_get_extend (pattern); cairo_p2d = pattern->matrix; status = cairo_matrix_invert (&cairo_p2d); @@ -3360,32 +4016,38 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, - &pattern_width, &pattern_height, - &x_offset, &y_offset, - &source, - &image_extra); + &pattern_extents, + &bounded, + &src_op_extents, + &source_surface, + &x_offset, &y_offset); if (unlikely (status)) return status; - if (pattern->extend == CAIRO_EXTEND_PAD) { + if (extend == CAIRO_EXTEND_PAD) { cairo_image_surface_t *img; - assert (source->type == CAIRO_SURFACE_TYPE_IMAGE); - img = (cairo_image_surface_t *) source; + assert (source_surface->type == CAIRO_SURFACE_TYPE_IMAGE); + img = (cairo_image_surface_t *) source_surface; status = _cairo_ps_surface_create_padded_image_from_image (surface, img, &pattern->matrix, extents, - &pattern_width, &pattern_height, - &x_offset, &y_offset, - &image); + &image, + &pattern_extents); if (unlikely (status)) goto release_source; } if (unlikely (status)) goto release_source; - switch (pattern->extend) { + if (!bounded) + { + extend = CAIRO_EXTEND_NONE; + _cairo_rectangle_intersect (&pattern_extents, &src_op_extents); + } + + switch (extend) { case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_NONE: { @@ -3402,7 +4064,8 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, * repeat visibly. */ double x1 = 0.0, y1 = 0.0; - double x2 = surface->width, y2 = surface->height; + double x2 = surface->surface_extents.width; + double y2 = surface->surface_extents.height; _cairo_matrix_transform_bounding_box (&pattern->matrix, &x1, &y1, &x2, &y2, NULL); @@ -3412,16 +4075,16 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, * required an answer that's large enough, we don't really * care if it's not as tight as possible.*/ xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + - pattern_width + pattern_height); + pattern_extents.width + pattern_extents.height); break; } case CAIRO_EXTEND_REPEAT: - xstep = pattern_width; - ystep = pattern_height; + xstep = pattern_extents.width; + ystep = pattern_extents.height; break; case CAIRO_EXTEND_REFLECT: - xstep = pattern_width*2; - ystep = pattern_height*2; + xstep = pattern_extents.width*2; + ystep = pattern_extents.height*2; break; /* All the rest (if any) should have been analyzed away, so these * cases should be unreachable. */ @@ -3432,27 +4095,49 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, } _cairo_output_stream_printf (surface->stream, - "/CairoPattern {\n"); + "/CairoPattern {\n" + "q %d %d %d %d rectclip\n", + pattern_extents.x, pattern_extents.y, + pattern_extents.width, pattern_extents.height); + + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) + src_op_extents = pattern_extents; + + old_paint_proc = surface->paint_proc; + surface->paint_proc = TRUE; + params.src_surface = image ? &image->base : source_surface; + params.op = op; + params.src_surface_extents = &pattern_extents; + params.src_surface_bounded = bounded; + params.src_op_extents = &src_op_extents; + params.filter = pattern->filter; + params.stencil_mask = FALSE; + params.is_image = FALSE; + params.approx_size = 0; + status = _cairo_ps_surface_emit_surface (surface, CAIRO_EMIT_SURFACE_ANALYZE, ¶ms); + if (unlikely (status)) + goto release_source; + + if (params.is_image) { + _cairo_output_stream_printf (surface->stream, + "[ %d 0 0 %d 0 0 ] concat\n", + pattern_extents.width, pattern_extents.height); + } - old_use_string_datasource = surface->use_string_datasource; - surface->use_string_datasource = TRUE; if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, - "%d g 0 0 %f %f rectfill\n", + "%d g %d %d %f %f rectfill\n", surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, + pattern_extents.x, pattern_extents.y, xstep, ystep); } - status = _cairo_ps_surface_emit_surface (surface, - pattern, - image ? &image->base : source, - op, - pattern_width, pattern_height, FALSE); + + status = _cairo_ps_surface_emit_surface (surface, CAIRO_EMIT_SURFACE_EMIT, ¶ms); if (unlikely (status)) goto release_source; - surface->use_string_datasource = old_use_string_datasource; _cairo_output_stream_printf (surface->stream, - "} bind def\n"); + " Q } bind def\n"); _cairo_output_stream_printf (surface->stream, "<< /PatternType 1\n" @@ -3462,20 +4147,43 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, " /XStep %f /YStep %f\n", xstep, ystep); - if (pattern->extend == CAIRO_EXTEND_REFLECT) { + if (extend == CAIRO_EXTEND_REFLECT) { + cairo_matrix_t mat; + _cairo_output_stream_printf (surface->stream, - " /BBox [0 0 %d %d]\n" + " /BBox [%d %d %d %d]\n" " /PaintProc {\n" - " pop CairoPattern\n" - " [-1 0 0 1 %d 0] concat CairoPattern\n" - " [ 1 0 0 -1 0 %d] concat CairoPattern\n" - " [-1 0 0 1 %d 0] concat CairoPattern\n" - " CairoPattern\n" - " } bind\n", - pattern_width*2, pattern_height*2, - pattern_width*2, - pattern_height*2, - pattern_width*2); + " pop CairoPattern\n", + pattern_extents.x, + pattern_extents.y, + pattern_extents.x + pattern_extents.width*2, + pattern_extents.y + pattern_extents.height*2); + + cairo_matrix_init_translate (&mat, pattern_extents.x, pattern_extents.y); + cairo_matrix_scale (&mat, -1, 1); + cairo_matrix_translate (&mat, -2*pattern_extents.width, 0); + cairo_matrix_translate (&mat, -pattern_extents.x, -pattern_extents.y); + _cairo_output_stream_printf (surface->stream, " q ["); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, "] concat CairoPattern Q\n"); + + cairo_matrix_init_translate (&mat, pattern_extents.x, pattern_extents.y); + cairo_matrix_scale (&mat, 1, -1); + cairo_matrix_translate (&mat, 0, -2*pattern_extents.height); + cairo_matrix_translate (&mat, -pattern_extents.x, -pattern_extents.y); + _cairo_output_stream_printf (surface->stream, " q ["); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, "] concat CairoPattern Q\n"); + + cairo_matrix_init_translate (&mat, pattern_extents.x, pattern_extents.y); + cairo_matrix_scale (&mat, -1, -1); + cairo_matrix_translate (&mat, -2*pattern_extents.width, -2*pattern_extents.height); + cairo_matrix_translate (&mat, -pattern_extents.x, -pattern_extents.y); + _cairo_output_stream_printf (surface->stream, " q ["); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, "] concat CairoPattern Q\n"); + + _cairo_output_stream_printf (surface->stream, " } bind\n"); } else { if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, @@ -3483,8 +4191,11 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, xstep, ystep); } else { _cairo_output_stream_printf (surface->stream, - " /BBox [0 0 %d %d]\n", - pattern_width, pattern_height); + " /BBox [%d %d %d %d]\n", + pattern_extents.x, + pattern_extents.y, + pattern_extents.x + pattern_extents.width, + pattern_extents.y + pattern_extents.height); } _cairo_output_stream_printf (surface->stream, " /PaintProc { pop CairoPattern }\n"); @@ -3499,11 +4210,13 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_init_identity (&ps_p2d); - cairo_matrix_translate (&ps_p2d, 0.0, surface->height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); - cairo_matrix_translate (&ps_p2d, 0.0, pattern_height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + cairo_matrix_translate (&ps_p2d, x_offset, y_offset); + if (((cairo_surface_pattern_t *)pattern)->surface->type != CAIRO_SURFACE_TYPE_RECORDING) + { + cairo_matrix_translate (&ps_p2d, 0.0, pattern_extents.height); + cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + } _cairo_output_stream_printf (surface->stream, "[ "); _cairo_output_stream_print_matrix (surface->stream, &ps_p2d); @@ -3511,11 +4224,13 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, " ]\n" "makepattern setpattern\n"); + surface->paint_proc = old_paint_proc; + release_source: if (image) cairo_surface_destroy (&image->base); - _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra); + _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source_surface); return status; } @@ -4098,7 +4813,7 @@ _cairo_ps_surface_paint_pattern (cairo_ps_surface_t *surface, case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _cairo_ps_surface_paint_surface (surface, - (cairo_pattern_t *)source, + source, extents, op, stencil_mask); @@ -4107,8 +4822,8 @@ _cairo_ps_surface_paint_pattern (cairo_ps_surface_t *surface, case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: return _cairo_ps_surface_paint_gradient (surface, - source, - extents); + source, + extents); case CAIRO_PATTERN_TYPE_SOLID: default: @@ -4146,17 +4861,10 @@ _cairo_ps_surface_get_extents (void *abstract_surface, { cairo_ps_surface_t *surface = abstract_surface; - rectangle->x = 0; - rectangle->y = 0; - - /* XXX: The conversion to integers here is pretty bogus, (not to - * mention the aribitray limitation of width to a short(!). We - * may need to come up with a better interface for get_extents. - */ - rectangle->width = ceil (surface->width); - rectangle->height = ceil (surface->height); + if (surface->surface_bounded) + *rectangle = surface->surface_extents; - return TRUE; + return surface->surface_bounded; } static void @@ -4240,8 +4948,11 @@ _cairo_ps_surface_paint (void *abstract_surface, if (unlikely (status)) goto cleanup_composite; - _cairo_output_stream_printf (stream, "0 0 %f %f rectfill\n", - surface->width, surface->height); + _cairo_output_stream_printf (stream, "%d %d %d %d rectfill\n", + surface->surface_extents.x, + surface->surface_extents.y, + surface->surface_extents.width, + surface->surface_extents.height); } cleanup_composite: @@ -4536,7 +5247,7 @@ _cairo_ps_surface_get_supported_mime_types (void *abstract_surface) return _cairo_ps_supported_mime_types; } -static void +static cairo_int_status_t _cairo_ps_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { @@ -4545,34 +5256,57 @@ _cairo_ps_surface_set_paginated_mode (void *abstract_surface, surface->paginated_mode = paginated_mode; - if (surface->clipper.clip != NULL) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); - _cairo_output_stream_printf (surface->stream, "Q q\n"); - _cairo_surface_clipper_reset (&surface->clipper); + if (surface->clipper.clip != NULL) + { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + + _cairo_output_stream_printf (surface->stream, "Q q\n"); + _cairo_surface_clipper_reset (&surface->clipper); + } } + + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_ps_surface_set_bounding_box (void *abstract_surface, - cairo_box_t *bbox) + cairo_box_t *analysis_bbox) { cairo_ps_surface_t *surface = abstract_surface; int i, num_comments; char **comments; - int x1, y1, x2, y2; cairo_bool_t has_page_media, has_page_bbox; const char *page_media; + cairo_rectangle_int_t page_bbox; + cairo_point_int_t bbox_p1, bbox_p2; /* in PS coordinates */ - x1 = floor (_cairo_fixed_to_double (bbox->p1.x)); - y1 = floor (surface->height - _cairo_fixed_to_double (bbox->p2.y)); - x2 = ceil (_cairo_fixed_to_double (bbox->p2.x)); - y2 = ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y)); + _cairo_box_round_to_rectangle (analysis_bbox, &page_bbox); - surface->page_bbox.x = x1; - surface->page_bbox.y = y1; - surface->page_bbox.width = x2 - x1; - surface->page_bbox.height = y2 - y1; + /* convert to PS coordinates */ + bbox_p1.x = page_bbox.x; + bbox_p1.y = ceil(surface->height) - (page_bbox.y + page_bbox.height); + bbox_p2.x = page_bbox.x + page_bbox.width; + bbox_p2.y = ceil(surface->height) - page_bbox.y; + + if (surface->num_pages == 1) { + surface->document_bbox_p1 = bbox_p1; + surface->document_bbox_p2 = bbox_p2; + } else { + if (bbox_p1.x < surface->document_bbox_p1.x) + surface->document_bbox_p1.x = bbox_p1.x; + if (bbox_p1.y < surface->document_bbox_p1.y) + surface->document_bbox_p1.y = bbox_p1.y; + if (bbox_p2.x < surface->document_bbox_p2.x) + surface->document_bbox_p2.x = bbox_p2.x; + if (bbox_p2.y < surface->document_bbox_p2.y) + surface->document_bbox_p2.y = bbox_p2.y; + } _cairo_output_stream_printf (surface->stream, "%%%%Page: %d %d\n", @@ -4613,7 +5347,10 @@ _cairo_ps_surface_set_bounding_box (void *abstract_surface, if (!has_page_bbox) { _cairo_output_stream_printf (surface->stream, "%%%%PageBoundingBox: %d %d %d %d\n", - x1, y1, x2, y2); + bbox_p1.x, + bbox_p1.y, + bbox_p2.x, + bbox_p2.y); } if (!surface->eps) { @@ -4625,27 +5362,14 @@ _cairo_ps_surface_set_bounding_box (void *abstract_surface, _cairo_output_stream_printf (surface->stream, "%%%%EndPageSetup\n" - "q %d %d %d %d rectclip q\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); + "q %d %d %d %d rectclip\n" + "1 0 0 -1 0 %f cm q\n", + bbox_p1.x, + bbox_p1.y, + bbox_p2.x - bbox_p1.x, + bbox_p2.y - bbox_p1.y, + ceil(surface->height)); - if (surface->num_pages == 1) { - surface->bbox_x1 = x1; - surface->bbox_y1 = y1; - surface->bbox_x2 = x2; - surface->bbox_y2 = y2; - } else { - if (x1 < surface->bbox_x1) - surface->bbox_x1 = x1; - if (y1 < surface->bbox_y1) - surface->bbox_y1 = y1; - if (x2 > surface->bbox_x2) - surface->bbox_x2 = x2; - if (y2 > surface->bbox_y2) - surface->bbox_y2 = y2; - } surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); |