diff options
Diffstat (limited to 'src/cairo-pdf-surface.c')
-rw-r--r-- | src/cairo-pdf-surface.c | 1119 |
1 files changed, 822 insertions, 297 deletions
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 2918523..896980a 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -50,6 +50,7 @@ #include "cairo-output-stream-private.h" #include "cairo-paginated-private.h" #include "cairo-scaled-font-subsets-private.h" +#include "cairo-surface-clipper-private.h" #include "cairo-type3-glyph-surface-private.h" #include <time.h> @@ -195,7 +196,7 @@ static cairo_status_t _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); static cairo_bool_t -_cairo_pdf_pattern_equal (const void *key_a, const void *key_b); +_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b); static const cairo_surface_backend_t cairo_pdf_surface_backend; static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend; @@ -243,6 +244,50 @@ _cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface, &surface->cairo_to_pdf); } +static cairo_bool_t +_path_covers_bbox (cairo_pdf_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + + return _cairo_path_fixed_is_box (path, &box) && + box.p1.x <= 0 && + box.p1.y <= 0 && + box.p2.x >= _cairo_fixed_from_double (surface->width) && + box.p2.y >= _cairo_fixed_from_double (surface->height); +} + +static cairo_status_t +_cairo_pdf_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_pdf_surface_t *surface = cairo_container_of (clipper, + cairo_pdf_surface_t, + clipper); + cairo_int_status_t status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (path == NULL) { + _cairo_output_stream_printf (surface->output, "Q q\n"); + + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + return CAIRO_STATUS_SUCCESS; + } + + if (_path_covers_bbox (surface, path)) + return CAIRO_STATUS_SUCCESS; + + return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); +} + static cairo_surface_t * _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, double width, @@ -275,8 +320,9 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_array_init (&surface->knockout_group, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t)); - surface->all_patterns = _cairo_hash_table_create (_cairo_pdf_pattern_equal); - if (unlikely (surface->all_patterns == NULL)) { + _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t)); + surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal); + if (unlikely (surface->all_surfaces == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL0; } @@ -309,8 +355,12 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->force_fallbacks = FALSE; surface->select_pattern_gstate_saved = FALSE; surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; surface->header_emitted = FALSE; + _cairo_surface_clipper_init (&surface->clipper, + _cairo_pdf_surface_clipper_intersect_clip_path); + _cairo_pdf_operators_init (&surface->pdf_operators, surface->output, &surface->cairo_to_pdf, @@ -323,7 +373,6 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->paginated_surface = _cairo_paginated_surface_create ( &surface->base, CAIRO_CONTENT_COLOR_ALPHA, - width, height, &cairo_pdf_surface_paginated_backend); status = surface->paginated_surface->status; @@ -336,7 +385,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, BAIL2: _cairo_scaled_font_subsets_destroy (surface->font_subsets); BAIL1: - _cairo_hash_table_destroy (surface->all_patterns); + _cairo_hash_table_destroy (surface->all_surfaces); BAIL0: _cairo_array_fini (&surface->objects); free (surface); @@ -570,11 +619,6 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface, _cairo_pdf_surface_set_size_internal (pdf_surface, width_in_points, height_in_points); - status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface, - width_in_points, - height_in_points); - if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); } static void @@ -582,6 +626,7 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) { int i, size; cairo_pdf_pattern_t *pattern; + cairo_pdf_source_surface_t *src_surface; cairo_pdf_smask_group_t *group; size = _cairo_array_num_elements (&surface->page_patterns); @@ -591,6 +636,13 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) } _cairo_array_truncate (&surface->page_patterns, 0); + size = _cairo_array_num_elements (&surface->page_surfaces); + for (i = 0; i < size; i++) { + src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i); + cairo_surface_destroy (src_surface->surface); + } + _cairo_array_truncate (&surface->page_surfaces, 0); + size = _cairo_array_num_elements (&surface->smask_groups); for (i = 0; i < size; i++) { _cairo_array_copy_element (&surface->smask_groups, i, &group); @@ -603,6 +655,11 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) static void _cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res) { + int i; + + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) + res->operators[i] = FALSE; + _cairo_array_init (&res->alphas, sizeof (double)); _cairo_array_init (&res->smasks, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&res->patterns, sizeof (cairo_pdf_resource_t)); @@ -623,6 +680,11 @@ _cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res) static void _cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res) { + int i; + + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) + res->operators[i] = FALSE; + _cairo_array_truncate (&res->alphas, 0); _cairo_array_truncate (&res->smasks, 0); _cairo_array_truncate (&res->patterns, 0); @@ -630,6 +692,15 @@ _cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res) _cairo_array_truncate (&res->fonts, 0); } +static void +_cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface, + cairo_operator_t op) +{ + cairo_pdf_group_resources_t *res = &surface->resources; + + res->operators[op] = TRUE; +} + static cairo_status_t _cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha, @@ -738,6 +809,47 @@ _cairo_pdf_surface_get_font_resource (cairo_pdf_surface_t *surface, return font.subset_resource; } +static const char * +_cairo_operator_to_pdf_blend_mode (cairo_operator_t op) +{ + switch (op) { + /* The extend blend mode operators */ + case CAIRO_OPERATOR_MULTIPLY: return "Multiply"; + case CAIRO_OPERATOR_SCREEN: return "Screen"; + case CAIRO_OPERATOR_OVERLAY: return "Overlay"; + case CAIRO_OPERATOR_DARKEN: return "Darken"; + case CAIRO_OPERATOR_LIGHTEN: return "Lighten"; + case CAIRO_OPERATOR_COLOR_DODGE: return "ColorDodge"; + case CAIRO_OPERATOR_COLOR_BURN: return "ColorBurn"; + case CAIRO_OPERATOR_HARD_LIGHT: return "HardLight"; + case CAIRO_OPERATOR_SOFT_LIGHT: return "SoftLight"; + case CAIRO_OPERATOR_DIFFERENCE: return "Difference"; + case CAIRO_OPERATOR_EXCLUSION: return "Exclusion"; + case CAIRO_OPERATOR_HSL_HUE: return "Hue"; + case CAIRO_OPERATOR_HSL_SATURATION: return "Saturation"; + case CAIRO_OPERATOR_HSL_COLOR: return "Color"; + case CAIRO_OPERATOR_HSL_LUMINOSITY: return "Luminosity"; + + default: + /* The original Porter-Duff set */ + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + return "Normal"; + } +} + static void _cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, cairo_pdf_group_resources_t *res) @@ -755,6 +867,14 @@ _cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, " /ExtGState <<\n"); + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) { + if (res->operators[i]) { + _cairo_output_stream_printf (surface->output, + " /b%d << /BM /%s >>\n", + i, _cairo_operator_to_pdf_blend_mode(i)); + } + } + for (i = 0; i < num_alphas; i++) { _cairo_array_copy_element (&res->alphas, i, &alpha); _cairo_output_stream_printf (surface->output, @@ -873,30 +993,180 @@ _cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t *surface, } static cairo_bool_t -_cairo_pdf_pattern_equal (const void *key_a, const void *key_b) +_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b) { - const cairo_pdf_pattern_entry_t *a = key_a; - const cairo_pdf_pattern_entry_t *b = key_b; + const cairo_pdf_source_surface_entry_t *a = key_a; + const cairo_pdf_source_surface_entry_t *b = key_b; - return a->id == b->id; + return (a->id == b->id) && (a->interpolate == b->interpolate); } static void -_cairo_pdf_pattern_init_key (cairo_pdf_pattern_entry_t *key) +_cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key) { key->base.hash = key->id; } +static cairo_int_status_t +_get_jpx_image_info (cairo_surface_t *source, + cairo_image_info_t *info, + const unsigned char **mime_data, + unsigned int *mime_data_length) +{ + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, + mime_data, mime_data_length); + if (*mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return _cairo_image_info_get_jpx_info (info, *mime_data, *mime_data_length); +} + +static cairo_int_status_t +_get_jpeg_image_info (cairo_surface_t *source, + cairo_image_info_t *info, + const unsigned char **mime_data, + unsigned int *mime_data_length) +{ + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + mime_data, mime_data_length); + if (*mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return _cairo_image_info_get_jpeg_info (info, *mime_data, *mime_data_length); +} + +static cairo_status_t +_get_source_surface_size (cairo_surface_t *source, + int *width, + int *height) +{ + cairo_status_t status; + cairo_rectangle_int_t extents; + cairo_image_info_t info; + const unsigned char *mime_data; + unsigned int mime_data_length; + + if (_cairo_surface_is_meta (source)) { + cairo_meta_surface_t *meta_surface = (cairo_meta_surface_t *) source; + cairo_box_t bbox; + + status = _cairo_meta_surface_get_bbox (meta_surface, &bbox, NULL); + if (unlikely (status)) + return status; + + _cairo_box_round_to_rectangle (&bbox, &extents); + + *width = extents.width; + *height = extents.height; + return CAIRO_STATUS_SUCCESS; + } + + status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *width = info.width; + *height = info.height; + return status; + } + + status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *width = info.width; + *height = info.height; + return status; + } + + if (! _cairo_surface_get_extents (source, &extents)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *width = extents.width; + *height = extents.height; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_filter_t filter, + cairo_pdf_resource_t *surface_res, + int *width, + int *height) +{ + cairo_pdf_source_surface_t src_surface; + cairo_pdf_source_surface_entry_t surface_key; + cairo_pdf_source_surface_entry_t *surface_entry; + cairo_status_t status; + cairo_bool_t 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; + } + + surface_key.id = source->unique_id; + surface_key.interpolate = interpolate; + _cairo_pdf_source_surface_init_key (&surface_key); + surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base); + if (surface_entry) { + *surface_res = surface_entry->surface_res; + *width = surface_entry->width; + *height = surface_entry->height; + + return CAIRO_STATUS_SUCCESS; + } + + surface_entry = malloc (sizeof (cairo_pdf_source_surface_entry_t)); + if (surface_entry == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + surface_entry->id = surface_key.id; + surface_entry->interpolate = interpolate; + _cairo_pdf_source_surface_init_key (surface_entry); + + src_surface.hash_entry = surface_entry; + src_surface.surface = cairo_surface_reference (source); + surface_entry->surface_res = _cairo_pdf_surface_new_object (surface); + if (surface_entry->surface_res.id == 0) { + free (surface_entry); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + status = _get_source_surface_size (source, &surface_entry->width, + &surface_entry->height); + + status = _cairo_array_append (&surface->page_surfaces, &src_surface); + if (unlikely (status)) { + free (surface_entry); + return status; + } + + status = _cairo_hash_table_insert (surface->all_surfaces, + &surface_entry->base); + + *surface_res = surface_entry->surface_res; + *width = surface_entry->width; + *height = surface_entry->height; + + return status; +} + static cairo_status_t _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents, + const cairo_rectangle_int_t *extents, cairo_pdf_resource_t *pattern_res, cairo_pdf_resource_t *gstate_res) { cairo_pdf_pattern_t pdf_pattern; - cairo_pdf_pattern_entry_t pdf_pattern_key; - cairo_pdf_pattern_entry_t *pdf_pattern_entry; cairo_status_t status; /* Solid colors are emitted into the content stream */ @@ -906,7 +1176,6 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; } - pdf_pattern_key.id = 0; if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { @@ -926,50 +1195,24 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, } } - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; - - pdf_pattern_key.id = surface->surface->unique_id; - } - - _cairo_pdf_pattern_init_key (&pdf_pattern_key); - if (pdf_pattern_key.base.hash != 0 && - (pdf_pattern_entry = _cairo_hash_table_lookup (surface->all_patterns, - &pdf_pattern_key.base))) - { - *pattern_res = pdf_pattern_entry->pattern_res; - *gstate_res = pdf_pattern_entry->gstate_res; - - return CAIRO_STATUS_SUCCESS; - } - - pdf_pattern_entry = malloc (sizeof (cairo_pdf_pattern_entry_t)); - if (pdf_pattern_entry == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - pdf_pattern_entry->id = pdf_pattern_key.id; - _cairo_pdf_pattern_init_key (pdf_pattern_entry); - - pdf_pattern.hash_entry = pdf_pattern_entry; - status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern); if (unlikely (status)) return status; - pdf_pattern_entry->pattern_res = _cairo_pdf_surface_new_object (surface); - if (pdf_pattern_entry->pattern_res.id == 0) { + pdf_pattern.pattern_res = _cairo_pdf_surface_new_object (surface); + if (pdf_pattern.pattern_res.id == 0) { cairo_pattern_destroy (pdf_pattern.pattern); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - pdf_pattern_entry->gstate_res.id = 0; + pdf_pattern.gstate_res.id = 0; /* gradient patterns require an smask object to implement transparency */ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { if (_cairo_pattern_is_opaque (pattern) == FALSE) { - pdf_pattern_entry->gstate_res = _cairo_pdf_surface_new_object (surface); - if (pdf_pattern_entry->gstate_res.id == 0) { + pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface); + if (pdf_pattern.gstate_res.id == 0) { cairo_pattern_destroy (pdf_pattern.pattern); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -978,7 +1221,7 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, pdf_pattern.width = surface->width; pdf_pattern.height = surface->height; - if (extents) { + if (extents != NULL) { pdf_pattern.extents = *extents; } else { pdf_pattern.extents.x = 0; @@ -987,8 +1230,8 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, pdf_pattern.extents.height = surface->height; } - *pattern_res = pdf_pattern_entry->pattern_res; - *gstate_res = pdf_pattern_entry->gstate_res; + *pattern_res = pdf_pattern.pattern_res; + *gstate_res = pdf_pattern.gstate_res; status = _cairo_array_append (&surface->page_patterns, &pdf_pattern); if (unlikely (status)) { @@ -996,13 +1239,6 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, return status; } - if (pdf_pattern_entry->base.hash != 0) { - status = _cairo_hash_table_insert (surface->all_patterns, - &pdf_pattern_entry->base); - if (unlikely (status)) - return status; - } - return CAIRO_STATUS_SUCCESS; } @@ -1041,6 +1277,7 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, surface->pdf_stream.length = length; surface->pdf_stream.compressed = compressed; surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; _cairo_pdf_operators_reset (&surface->pdf_operators); _cairo_output_stream_printf (surface->output, @@ -1181,6 +1418,7 @@ _cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, surface->group_stream.active = TRUE; surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; _cairo_pdf_operators_reset (&surface->pdf_operators); surface->group_stream.mem_stream = _cairo_memory_stream_create (); @@ -1267,6 +1505,7 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, static cairo_status_t _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t *resource, cairo_bool_t is_form) { cairo_status_t status; @@ -1281,7 +1520,7 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, if (is_form) { status = _cairo_pdf_surface_open_stream (surface, - NULL, + resource, surface->compress_content, " /Type /XObject\n" " /Subtype /Form\n" @@ -1298,7 +1537,7 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, } else { status = _cairo_pdf_surface_open_stream (surface, - NULL, + resource, surface->compress_content, NULL); } @@ -1340,23 +1579,14 @@ _cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface) return _cairo_output_stream_get_status (surface->output); } -static cairo_surface_t * -_cairo_pdf_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - return _cairo_meta_surface_create (content, width, height); -} - static void -_cairo_pdf_pattern_entry_pluck (void *entry, void *closure) +_cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) { - cairo_pdf_pattern_entry_t *pattern_entry = entry; + cairo_pdf_source_surface_entry_t *surface_entry = entry; cairo_hash_table_t *patterns = closure; - _cairo_hash_table_remove (patterns, &pattern_entry->base); - free (pattern_entry); + _cairo_hash_table_remove (patterns, &surface_entry->base); + free (surface_entry); } static cairo_status_t @@ -1439,10 +1669,11 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->rgb_linear_functions); _cairo_array_fini (&surface->alpha_linear_functions); _cairo_array_fini (&surface->page_patterns); - _cairo_hash_table_foreach (surface->all_patterns, - _cairo_pdf_pattern_entry_pluck, - surface->all_patterns); - _cairo_hash_table_destroy (surface->all_patterns); + _cairo_array_fini (&surface->page_surfaces); + _cairo_hash_table_foreach (surface->all_surfaces, + _cairo_pdf_source_surface_entry_pluck, + surface->all_surfaces); + _cairo_hash_table_destroy (surface->all_surfaces); _cairo_array_fini (&surface->smask_groups); _cairo_array_fini (&surface->fonts); _cairo_array_fini (&surface->knockout_group); @@ -1452,6 +1683,8 @@ _cairo_pdf_surface_finish (void *abstract_surface) surface->font_subsets = NULL; } + _cairo_surface_clipper_reset (&surface->clipper); + return status; } @@ -1494,7 +1727,7 @@ _cairo_pdf_surface_has_fallback_images (void *abstract_surface, cairo_pdf_surface_t *surface = abstract_surface; surface->has_fallback_images = has_fallbacks; - status = _cairo_pdf_surface_open_content_stream (surface, has_fallbacks); + status = _cairo_pdf_surface_open_content_stream (surface, NULL, has_fallbacks); if (unlikely (status)) return status; @@ -1614,7 +1847,7 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, static cairo_status_t _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, cairo_image_surface_t *image, - cairo_pdf_resource_t *image_ret, + cairo_pdf_resource_t *image_res, cairo_filter_t filter) { cairo_status_t status = CAIRO_STATUS_SUCCESS; @@ -1712,7 +1945,7 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, if (need_smask) status = _cairo_pdf_surface_open_stream (surface, - NULL, + image_res, TRUE, IMAGE_DICTIONARY " /SMask %d 0 R\n", @@ -1721,7 +1954,7 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, smask.id); else status = _cairo_pdf_surface_open_stream (surface, - NULL, + image_res, TRUE, IMAGE_DICTIONARY, image->width, image->height, @@ -1731,7 +1964,6 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, #undef IMAGE_DICTIONARY - *image_ret = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, rgb, rgb_size); status = _cairo_pdf_surface_close_stream (surface); @@ -1744,9 +1976,7 @@ CLEANUP: static cairo_int_status_t _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, cairo_surface_t *source, - cairo_pdf_resource_t *res, - int *width, - int *height) + cairo_pdf_resource_t res) { cairo_status_t status; const unsigned char *mime_data; @@ -1766,7 +1996,7 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, return status; status = _cairo_pdf_surface_open_stream (surface, - NULL, + &res, FALSE, " /Type /XObject\n" " /Subtype /Image\n" @@ -1779,23 +2009,17 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, if (status) return status; - *res = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, mime_data, mime_data_length); _cairo_output_stream_printf (surface->output, "\n"); status = _cairo_pdf_surface_close_stream (surface); - *width = info.width; - *height = info.height; - return status; } static cairo_int_status_t _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, cairo_surface_t *source, - cairo_pdf_resource_t *res, - int *width, - int *height) + cairo_pdf_resource_t res) { cairo_status_t status; const unsigned char *mime_data; @@ -1817,7 +2041,7 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_pdf_surface_open_stream (surface, - NULL, + &res, FALSE, " /Type /XObject\n" " /Subtype /Image\n" @@ -1833,90 +2057,130 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; - *res = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, mime_data, mime_data_length); _cairo_output_stream_printf (surface->output, "\n"); status = _cairo_pdf_surface_close_stream (surface); - *width = info.width; - *height = info.height; - return status; } static cairo_status_t _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern, - cairo_pdf_resource_t *resource, - int *width, - int *height, - int *origin_x, - int *origin_y) + cairo_surface_t *source, + cairo_pdf_resource_t resource, + cairo_bool_t interpolate) { cairo_image_surface_t *image; - cairo_surface_t *pad_image; void *image_extra; cairo_status_t status; - cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern; - int x = 0; - int y = 0; - status = _cairo_pdf_surface_emit_jpx_image (surface, pattern->surface, - resource, width, height); + status = _cairo_pdf_surface_emit_jpx_image (surface, source, resource); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - status = _cairo_pdf_surface_emit_jpeg_image (surface, pattern->surface, - resource, width, height); + status = _cairo_pdf_surface_emit_jpeg_image (surface, source, resource); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); + status = _cairo_surface_acquire_source_image (source, &image, &image_extra); if (unlikely (status)) return status; + status = _cairo_pdf_surface_emit_image (surface, image, + &resource, interpolate); + if (unlikely (status)) + goto BAIL; + +BAIL: + _cairo_surface_release_source_image (source, image, image_extra); + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern, + cairo_pdf_resource_t *resource, + int *width, + int *height, + int *origin_x, + int *origin_y) +{ + cairo_image_surface_t *image; + cairo_surface_t *pad_image; + void *image_extra; + cairo_status_t status; + cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern; + int x = 0; + int y = 0; + cairo_bool_t interpolate; + + status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); + if (unlikely (status)) + return status; + pad_image = &image->base; - if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) { - cairo_box_t box; - cairo_rectangle_int_t rect; - cairo_surface_pattern_t pad_pattern; - - /* get the operation extents in pattern space */ - _cairo_box_from_rectangle (&box, &pdf_pattern->extents); - _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL); - _cairo_box_round_to_rectangle (&box, &rect); - x = -rect.x; - y = -rect.y; - - pad_image = _cairo_image_surface_create_with_content (pattern->surface->content, - rect.width, - rect.height); - if (pad_image->status) { - status = pad_image->status; - goto BAIL; - } + if (pattern->base.extend == CAIRO_EXTEND_PAD) { + cairo_box_t box; + cairo_rectangle_int_t rect; + cairo_surface_pattern_t pad_pattern; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&box, &pdf_pattern->extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &rect); + x = -rect.x; + y = -rect.y; + + pad_image = _cairo_image_surface_create_with_content (pattern->surface->content, + rect.width, + rect.height); + if (pad_image->status) { + status = pad_image->status; + goto BAIL; + } - _cairo_pattern_init_for_surface (&pad_pattern, &image->base); - cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); - pad_pattern.base.extend = CAIRO_EXTEND_PAD; - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pad_pattern.base, - NULL, - pad_image, - 0, 0, - 0, 0, - 0, 0, - rect.width, - rect.height); - _cairo_pattern_fini (&pad_pattern.base); - if (unlikely (status)) - goto BAIL; + _cairo_pattern_init_for_surface (&pad_pattern, &image->base); + cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); + pad_pattern.base.extend = CAIRO_EXTEND_PAD; + status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, + &pad_pattern.base, + NULL, + pad_image, + 0, 0, + 0, 0, + 0, 0, + rect.width, + rect.height, + NULL); + _cairo_pattern_fini (&pad_pattern.base); + if (unlikely (status)) + goto BAIL; + } + + switch (pdf_pattern->pattern->filter) { + 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; + } + + *resource = _cairo_pdf_surface_new_object (surface); + if (resource->id == 0) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; } status = _cairo_pdf_surface_emit_image (surface, (cairo_image_surface_t *)pad_image, - resource, pattern->base.filter); + resource, interpolate); if (unlikely (status)) - goto BAIL; + goto BAIL; *width = ((cairo_image_surface_t *)pad_image)->width; *height = ((cairo_image_surface_t *)pad_image)->height; @@ -1925,33 +2189,33 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, BAIL: if (pad_image != &image->base) - cairo_surface_destroy (pad_image); + cairo_surface_destroy (pad_image); _cairo_surface_release_source_image (pattern->surface, image, image_extra); return status; } + static cairo_status_t _cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface, cairo_surface_t *meta_surface, - cairo_pdf_resource_t *resource) + cairo_pdf_resource_t resource) { double old_width, old_height; cairo_paginated_mode_t old_paginated_mode; - cairo_clip_t *old_clip; cairo_rectangle_int_t meta_extents; + cairo_bool_t is_bounded; cairo_status_t status; int alpha = 0; - status = _cairo_surface_get_extents (meta_surface, &meta_extents); - if (unlikely (status)) - return status; + is_bounded = _cairo_surface_get_extents (meta_surface, &meta_extents); + assert (is_bounded); old_width = surface->width; old_height = surface->height; old_paginated_mode = surface->paginated_mode; - old_clip = _cairo_surface_get_clip (&surface->base); + _cairo_pdf_surface_set_size_internal (surface, meta_extents.width, meta_extents.height); @@ -1961,11 +2225,10 @@ _cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface, */ surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; _cairo_pdf_group_resources_clear (&surface->resources); - status = _cairo_pdf_surface_open_content_stream (surface, TRUE); + status = _cairo_pdf_surface_open_content_stream (surface, &resource, TRUE); if (unlikely (status)) return status; - *resource = surface->content; if (cairo_surface_get_content (meta_surface) == CAIRO_CONTENT_COLOR) { status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); if (unlikely (status)) @@ -1984,10 +2247,6 @@ _cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; - status = _cairo_surface_set_clip (&surface->base, old_clip); - if (unlikely (status)) - return status; - status = _cairo_pdf_surface_close_content_stream (surface); _cairo_pdf_surface_set_size_internal (surface, @@ -1999,16 +2258,31 @@ _cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface, } static cairo_status_t +_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, + cairo_pdf_source_surface_t *src_surface) +{ + if (_cairo_surface_is_meta (src_surface->surface)) { + return _cairo_pdf_surface_emit_meta_surface (surface, + src_surface->surface, + src_surface->hash_entry->surface_res); + } else { + return _cairo_pdf_surface_emit_image_surface (surface, + src_surface->surface, + src_surface->hash_entry->surface_res, + src_surface->hash_entry->interpolate); + } +} + +static cairo_status_t _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern; cairo_status_t status; - cairo_pdf_resource_t pattern_resource = {0}; /* squelch bogus compiler warning */ + cairo_pdf_resource_t pattern_resource = {0}; cairo_matrix_t cairo_p2d, pdf_p2d; cairo_extend_t extend = cairo_pattern_get_extend (&pattern->base); double xstep, ystep; - cairo_rectangle_int_t surface_extents; int pattern_width = 0; /* squelch bogus compiler warning */ int pattern_height = 0; /* squelch bogus compiler warning */ int origin_x = 0; /* squelch bogus compiler warning */ @@ -2016,35 +2290,26 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, int bbox_x, bbox_y; char draw_surface[200]; - if (_cairo_surface_is_meta (pattern->surface)) { - cairo_surface_t *meta_surface = pattern->surface; - cairo_rectangle_int_t pattern_extents; - - status = _cairo_pdf_surface_emit_meta_surface (surface, - meta_surface, - &pattern_resource); - if (unlikely (status)) - return status; - - status = _cairo_surface_get_extents (meta_surface, &pattern_extents); - if (unlikely (status)) - return status; - - pattern_width = pattern_extents.width; - pattern_height = pattern_extents.height; - } else { - status = _cairo_pdf_surface_emit_image_surface (surface, - pdf_pattern, + if (pattern->base.extend == CAIRO_EXTEND_PAD && + ! _cairo_surface_is_meta (pattern->surface)) + { + status = _cairo_pdf_surface_emit_padded_image_surface (surface, + pdf_pattern, + &pattern_resource, + &pattern_width, + &pattern_height, + &origin_x, + &origin_y); + } + else + { + status = _cairo_pdf_surface_add_source_surface (surface, + pattern->surface, + pdf_pattern->pattern->filter, &pattern_resource, &pattern_width, - &pattern_height, - &origin_x, - &origin_y); - if (unlikely (status)) - return status; + &pattern_height); } - - status = _cairo_surface_get_extents (&surface->base, &surface_extents); if (unlikely (status)) return status; @@ -2130,17 +2395,14 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); - cairo_matrix_init_identity (&pdf_p2d); - cairo_matrix_translate (&pdf_p2d, 0.0, surface_extents.height); - cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); - cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); + cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf); cairo_matrix_translate (&pdf_p2d, -origin_x, -origin_y); cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height); cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); - _cairo_pdf_surface_update_object (surface, pdf_pattern->hash_entry->pattern_res); + _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); status = _cairo_pdf_surface_open_stream (surface, - &pdf_pattern->hash_entry->pattern_res, + &pdf_pattern->pattern_res, FALSE, " /PatternType 1\n" " /BBox [0 0 %d %d]\n" @@ -2687,9 +2949,9 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, dx = fabs (x2 - x1); dy = fabs (y2 - y1); if (dx > 1e-6) - x_rep = (int) ceil (surface->width/dx); + x_rep = ceil (surface->width/dx); if (dy > 1e-6) - y_rep = (int) ceil (surface->height/dy); + y_rep = ceil (surface->height/dy); repeat_end = MAX (x_rep, y_rep); repeat_begin = -repeat_end; @@ -2755,7 +3017,7 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, } } - _cairo_pdf_surface_update_object (surface, pdf_pattern->hash_entry->pattern_res); + _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Pattern\n" @@ -2767,7 +3029,7 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, " /Coords [ %f %f %f %f ]\n" " /Domain [ %f %f ]\n" " /Function %d 0 R\n", - pdf_pattern->hash_entry->pattern_res.id, + pdf_pattern->pattern_res.id, pat_to_pdf.xx, pat_to_pdf.yx, pat_to_pdf.xy, pat_to_pdf.yy, pat_to_pdf.x0, pat_to_pdf.y0, @@ -2791,7 +3053,7 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, if (alpha_function.id != 0) { cairo_pdf_resource_t mask_resource; - assert (pdf_pattern->hash_entry->gstate_res.id != 0); + assert (pdf_pattern->gstate_res.id != 0); /* Create pattern for SMask. */ mask_resource = _cairo_pdf_surface_new_object (surface); @@ -2834,7 +3096,7 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, return status; status = cairo_pdf_surface_emit_transparency_group (surface, - pdf_pattern->hash_entry->gstate_res, + pdf_pattern->gstate_res, mask_resource); if (unlikely (status)) return status; @@ -2878,7 +3140,7 @@ _cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, y2 = _cairo_fixed_to_double (pattern->c2.y); r2 = _cairo_fixed_to_double (pattern->r2); - _cairo_pdf_surface_update_object (surface, pdf_pattern->hash_entry->pattern_res); + _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" @@ -2890,7 +3152,7 @@ _cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, " /ColorSpace /DeviceRGB\n" " /Coords [ %f %f %f %f %f %f ]\n" " /Function %d 0 R\n", - pdf_pattern->hash_entry->pattern_res.id, + pdf_pattern->pattern_res.id, pat_to_pdf.xx, pat_to_pdf.yx, pat_to_pdf.xy, pat_to_pdf.yy, pat_to_pdf.x0, pat_to_pdf.y0, @@ -2913,7 +3175,7 @@ _cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, if (alpha_function.id != 0) { cairo_pdf_resource_t mask_resource; - assert (pdf_pattern->hash_entry->gstate_res.id != 0); + assert (pdf_pattern->gstate_res.id != 0); /* Create pattern for SMask. */ mask_resource = _cairo_pdf_surface_new_object (surface); @@ -2951,7 +3213,7 @@ _cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, "endobj\n"); status = cairo_pdf_surface_emit_transparency_group (surface, - pdf_pattern->hash_entry->gstate_res, + pdf_pattern->gstate_res, mask_resource); if (unlikely (status)) return status; @@ -3004,6 +3266,82 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern } static cairo_status_t +_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, + cairo_surface_pattern_t *source) +{ + cairo_pdf_resource_t surface_res; + int width, height; + cairo_matrix_t cairo_p2d, pdf_p2d; + cairo_status_t status; + int alpha; + + status = _cairo_pdf_surface_add_source_surface (surface, + source->surface, + source->base.filter, + &surface_res, + &width, + &height); + if (unlikely (status)) + return status; + + cairo_p2d = source->base.matrix; + status = cairo_matrix_invert (&cairo_p2d); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + pdf_p2d = surface->cairo_to_pdf; + cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); + cairo_matrix_translate (&pdf_p2d, 0.0, height); + cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); + if (! _cairo_surface_is_meta (source->surface)) + cairo_matrix_scale (&pdf_p2d, width, height); + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (! _cairo_matrix_is_identity (&pdf_p2d)) { + _cairo_output_stream_printf (surface->output, + "%f %f %f %f %f %f cm\n", + pdf_p2d.xx, pdf_p2d.yx, + pdf_p2d.xy, pdf_p2d.yy, + pdf_p2d.x0, pdf_p2d.y0); + } + + status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/a%d gs /x%d Do\n", + alpha, + surface_res.id); + + return _cairo_pdf_surface_add_xobject (surface, surface_res); +} + +static cairo_status_t +_cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, + cairo_operator_t op) +{ + cairo_status_t status; + + if (op == surface->current_operator) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/b%d gs\n", op); + surface->current_operator = op; + _cairo_pdf_surface_add_operator (surface, op); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, cairo_pdf_resource_t pattern_res, @@ -3148,7 +3486,7 @@ _cairo_pdf_surface_show_page (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t +static cairo_bool_t _cairo_pdf_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -3161,35 +3499,10 @@ _cairo_pdf_surface_get_extents (void *abstract_surface, * mention the arbitrary limitation of width to a short(!). We * may need to come up with a better interface for get_size. */ - rectangle->width = (int) ceil (surface->width); - rectangle->height = (int) ceil (surface->height); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_intersect_clip_path (void *abstract_surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_int_status_t status; - - if (path == NULL) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q q\n"); - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - return CAIRO_STATUS_SUCCESS; - } + rectangle->width = ceil (surface->width); + rectangle->height = ceil (surface->height); - return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); + return TRUE; } static void @@ -3414,37 +3727,62 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, "endcodespacerange\n"); - num_bfchar = font_subset->num_glyphs - 1; + if (font_subset->is_scaled) { + /* Type 3 fonts include glyph 0 in the subset */ + num_bfchar = font_subset->num_glyphs; - /* The CMap specification has a limit of 100 characters per beginbfchar operator */ - _cairo_output_stream_printf (surface->output, - "%d beginbfchar\n", - num_bfchar > 100 ? 100 : num_bfchar); + /* The CMap specification has a limit of 100 characters per beginbfchar operator */ + _cairo_output_stream_printf (surface->output, + "%d beginbfchar\n", + num_bfchar > 100 ? 100 : num_bfchar); + + for (i = 0; i < num_bfchar; i++) { + if (i != 0 && i % 100 == 0) { + _cairo_output_stream_printf (surface->output, + "endbfchar\n" + "%d beginbfchar\n", + num_bfchar - i > 100 ? 100 : num_bfchar - i); + } + _cairo_output_stream_printf (surface->output, "<%02x> ", i); + status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, + font_subset->utf8[i]); + if (unlikely (status)) + return status; - for (i = 0; i < num_bfchar; i++) { - if (i != 0 && i % 100 == 0) { - _cairo_output_stream_printf (surface->output, - "endbfchar\n" - "%d beginbfchar\n", - num_bfchar - i > 100 ? 100 : num_bfchar - i); - } - if (is_composite) { - _cairo_output_stream_printf (surface->output, - "<%04x> ", - i + 1); - } else { - _cairo_output_stream_printf (surface->output, - "<%02x> ", - i + 1); - } - status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, - font_subset->utf8[i + 1]); - if (unlikely (status)) - return status; + _cairo_output_stream_printf (surface->output, + "\n"); + } + } else { + /* Other fonts reserve glyph 0 for .notdef. Omit glyph 0 from the /ToUnicode map */ + num_bfchar = font_subset->num_glyphs - 1; + /* The CMap specification has a limit of 100 characters per beginbfchar operator */ _cairo_output_stream_printf (surface->output, - "\n"); + "%d beginbfchar\n", + num_bfchar > 100 ? 100 : num_bfchar); + + for (i = 0; i < num_bfchar; i++) { + if (i != 0 && i % 100 == 0) { + _cairo_output_stream_printf (surface->output, + "endbfchar\n" + "%d beginbfchar\n", + num_bfchar - i > 100 ? 100 : num_bfchar - i); + } + if (is_composite) + _cairo_output_stream_printf (surface->output, "<%04x> ", i + 1); + else + _cairo_output_stream_printf (surface->output, "<%02x> ", i + 1); + + status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, + font_subset->utf8[i + 1]); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "\n"); + } } + _cairo_output_stream_printf (surface->output, "endbfchar\n"); @@ -4521,8 +4859,10 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, group->height); /* _mask is a special case that requires two groups - source * and mask as well as a smask and gstate dictionary */ - if (group->operation == PDF_MASK) - return _cairo_pdf_surface_write_mask_group (surface, group); + if (group->operation == PDF_MASK) { + status = _cairo_pdf_surface_write_mask_group (surface, group); + goto RESTORE_SIZE; + } status = _cairo_pdf_surface_open_group (surface, &group->group_res); if (unlikely (status)) @@ -4574,6 +4914,7 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_close_group (surface, NULL); +RESTORE_SIZE: _cairo_pdf_surface_set_size_internal (surface, old_width, old_height); @@ -4586,7 +4927,8 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface { cairo_pdf_pattern_t pattern; cairo_pdf_smask_group_t *group; - int pattern_index, group_index; + cairo_pdf_source_surface_t src_surface; + int pattern_index, group_index, surface_index; cairo_status_t status; /* Writing out PDF_MASK groups will cause additional smask groups @@ -4598,8 +4940,10 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface */ pattern_index = 0; group_index = 0; + surface_index = 0; while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) || - (group_index < _cairo_array_num_elements (&surface->smask_groups))) + (group_index < _cairo_array_num_elements (&surface->smask_groups)) || + (surface_index < _cairo_array_num_elements (&surface->page_surfaces))) { for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) { _cairo_array_copy_element (&surface->smask_groups, group_index, &group); @@ -4614,6 +4958,13 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface if (unlikely (status)) return status; } + + for (; surface_index < _cairo_array_num_elements (&surface->page_surfaces); surface_index++) { + _cairo_array_copy_element (&surface->page_surfaces, surface_index, &src_surface); + status = _cairo_pdf_surface_emit_surface (surface, &src_surface); + if (unlikely (status)) + return status; + } } return CAIRO_STATUS_SUCCESS; @@ -4654,7 +5005,7 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) return status; _cairo_pdf_group_resources_clear (&surface->resources); - status = _cairo_pdf_surface_open_content_stream (surface, FALSE); + status = _cairo_pdf_surface_open_content_stream (surface, NULL, FALSE); if (unlikely (status)) return status; @@ -4821,6 +5172,46 @@ _pattern_supported (const cairo_pattern_t *pattern) return FALSE; } +static cairo_bool_t +_pdf_operator_supported (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return TRUE; + + default: + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + return FALSE; + } +} + static cairo_int_status_t _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, cairo_operator_t op, @@ -4835,7 +5226,7 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, if (! _pattern_supported (pattern)) return CAIRO_INT_STATUS_UNSUPPORTED; - if (op == CAIRO_OPERATOR_OVER) { + if (_pdf_operator_supported (op)) { if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; @@ -4846,10 +5237,10 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN; } } - } - if (op == CAIRO_OPERATOR_OVER) return CAIRO_STATUS_SUCCESS; + } + /* The SOURCE operator is supported if the pattern is opaque or if * there is nothing painted underneath. */ @@ -4911,19 +5302,20 @@ _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) return status; _cairo_pdf_group_resources_clear (&surface->resources); - return _cairo_pdf_surface_open_content_stream (surface, TRUE); + return _cairo_pdf_surface_open_content_stream (surface, NULL, TRUE); } static cairo_int_status_t _cairo_pdf_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { return _cairo_pdf_surface_analyze_operation (surface, op, source); @@ -4935,11 +5327,42 @@ _cairo_pdf_surface_paint (void *abstract_surface, assert (_cairo_pdf_surface_operation_supported (surface, op, source)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + source->extend == CAIRO_EXTEND_NONE) { + + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_surface_paint_surface_pattern (surface, + (cairo_surface_pattern_t *) source); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + return _cairo_output_stream_get_status (surface->output); + } + + status = _cairo_surface_paint_extents (&surface->base, + op, source, clip, + &extents); + if (unlikely (status)) + return status; + pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; @@ -4970,16 +5393,13 @@ _cairo_pdf_surface_paint (void *abstract_surface, if (unlikely (status)) return status; - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", gstate_res.id, group->group_res.id); } else { - status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); + status = _cairo_pdf_surface_select_pattern (surface, source, + pattern_res, FALSE); if (unlikely (status)) return status; @@ -5000,7 +5420,7 @@ _cairo_pdf_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; @@ -5028,6 +5448,10 @@ _cairo_pdf_surface_mask (void *abstract_surface, assert (_cairo_pdf_surface_operation_supported (surface, op, source)); assert (_cairo_pdf_surface_operation_supported (surface, op, mask)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + group = _cairo_pdf_surface_create_smask_group (surface); if (unlikely (group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -5067,6 +5491,10 @@ _cairo_pdf_surface_mask (void *abstract_surface, if (unlikely (status)) return status; + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", group->group_res.id, @@ -5085,27 +5513,44 @@ _cairo_pdf_surface_stroke (void *abstract_surface, cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_pdf_surface_analyze_operation (surface, op, source); assert (_cairo_pdf_surface_operation_supported (surface, op, source)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_surface_stroke_extents (&surface->base, + op, source, path, + style, ctm, ctm_inverse, + tolerance, antialias, + clip, &extents); + if (unlikely (status)) + return status; + pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface); if (unlikely (group == NULL)) @@ -5178,12 +5623,13 @@ _cairo_pdf_surface_fill (void *abstract_surface, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { return _cairo_pdf_surface_analyze_operation (surface, op, source); @@ -5195,11 +5641,49 @@ _cairo_pdf_surface_fill (void *abstract_surface, assert (_cairo_pdf_surface_operation_supported (surface, op, source)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + source->extend == CAIRO_EXTEND_NONE) { + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_operators_clip (&surface->pdf_operators, + path, + fill_rule); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_paint_surface_pattern (surface, + (cairo_surface_pattern_t *) source); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + return _cairo_output_stream_get_status (surface->output); + } + + status = _cairo_surface_fill_extents (&surface->base, + op, source, path, fill_rule, + tolerance, antialias, + clip, &extents); + if (unlikely (status)) + return status; + pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; @@ -5279,11 +5763,12 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res; + cairo_rectangle_int_t extents; /* During analysis we return unsupported and let the _fill and * _stroke functions that are on the fallback path do the analysis @@ -5304,10 +5789,29 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, return CAIRO_INT_STATUS_UNSUPPORTED; } + if (fill_op != stroke_op) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, fill_op); + if (unlikely (status)) + return status; + + status = _cairo_surface_fill_extents (&surface->base, + fill_op, fill_source, path, fill_rule, + fill_tolerance, fill_antialias, + clip, &extents); + if (unlikely (status)) + return status; + + fill_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, - extents, + &extents, &fill_pattern_res, &gstate_res); if (unlikely (status)) @@ -5315,11 +5819,19 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, assert (gstate_res.id == 0); + status = _cairo_surface_stroke_extents (&surface->base, + stroke_op, stroke_source, path, + stroke_style, stroke_ctm, stroke_ctm_inverse, + stroke_tolerance, stroke_antialias, + clip, &extents); + if (unlikely (status)) + return status; + stroke_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, stroke_source, - extents, + &extents, &stroke_pattern_res, &gstate_res); if (unlikely (status)) @@ -5373,27 +5885,43 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents) + cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; + cairo_rectangle_int_t extents; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_pdf_surface_analyze_operation (surface, op, source); assert (_cairo_pdf_surface_operation_supported (surface, op, source)); + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_surface_glyphs_extents (&surface->base, op, source, + glyphs, num_glyphs, + scaled_font, + clip, &extents); + if (unlikely (status)) + return status; + pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents, &pattern_res, &gstate_res); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface); if (unlikely (group == NULL)) @@ -5504,7 +6032,7 @@ _cairo_pdf_surface_set_paginated_mode (void *abstract_surface, static const cairo_surface_backend_t cairo_pdf_surface_backend = { CAIRO_SURFACE_TYPE_PDF, - _cairo_pdf_surface_create_similar, + NULL, /* create similar: handled by wrapper */ _cairo_pdf_surface_finish, NULL, /* acquire_source_image */ NULL, /* release_source_image */ @@ -5518,8 +6046,6 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { NULL, /* check_span_renderer */ NULL, /* _cairo_pdf_surface_copy_page */ _cairo_pdf_surface_show_page, - NULL, /* set_clip_region */ - _cairo_pdf_surface_intersect_clip_path, _cairo_pdf_surface_get_extents, NULL, /* old_show_glyphs */ _cairo_pdf_surface_get_font_options, @@ -5538,7 +6064,6 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { NULL, /* snapshot */ NULL, /* is_compatible */ - NULL, /* reset */ _cairo_pdf_surface_fill_stroke, NULL, /* create_solid_pattern_surface */ NULL, /* can_repaint_solid_pattern_surface */ |