diff options
Diffstat (limited to 'src/cairo-pdf-surface.c')
-rw-r--r--[-rwxr-xr-x] | src/cairo-pdf-surface.c | 1168 |
1 files changed, 851 insertions, 317 deletions
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index a0bf1c1d5..552e4bf4f 100755..100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -130,6 +130,23 @@ * * The PDF surface is used to render cairo graphics to Adobe * PDF files and is a multi-page vector surface backend. + * + * The following mime types are supported: %CAIRO_MIME_TYPE_JPEG, + * %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_UNIQUE_ID, + * %CAIRO_MIME_TYPE_JBIG2, %CAIRO_MIME_TYPE_JBIG2_GLOBAL, + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID. + * + * JBIG2 data in PDF must be in the embedded format as descibed in + * ISO/IEC 11544. Image specific JBIG2 data must be in + * %CAIRO_MIME_TYPE_JBIG2. Any global segments in the JBIG2 data + * (segments with page association field set to 0) must be in + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data may be shared by + * multiple images. All images sharing the same global data must set + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID to a unique identifer. At least + * one of the images must provide the global data using + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data will only be + * embedded once but shared by all JBIG2 images with the same + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID. **/ static cairo_bool_t @@ -164,6 +181,9 @@ static const char *_cairo_pdf_supported_mime_types[] = CAIRO_MIME_TYPE_JPEG, CAIRO_MIME_TYPE_JP2, CAIRO_MIME_TYPE_UNIQUE_ID, + CAIRO_MIME_TYPE_JBIG2, + CAIRO_MIME_TYPE_JBIG2_GLOBAL, + CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, NULL }; @@ -198,7 +218,7 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface); static void _cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group); -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_font (unsigned int font_id, unsigned int subset_id, void *closure); @@ -206,16 +226,16 @@ _cairo_pdf_surface_add_font (unsigned int font_id, static void _cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res); -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *resource, cairo_bool_t compressed, const char *fmt, ...) CAIRO_PRINTF_FORMAT(4, 5); -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface); -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); static void @@ -230,10 +250,10 @@ _cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface); static long _cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface); -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); static cairo_bool_t @@ -246,7 +266,7 @@ static cairo_pdf_resource_t _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t resource; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_object_t object; object.offset = _cairo_output_stream_get_position (surface->output); @@ -365,6 +385,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t)); _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t)); + _cairo_array_init (&surface->jbig2_global, sizeof (cairo_pdf_jbig2_global_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); @@ -410,7 +431,8 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_pdf_operators_init (&surface->pdf_operators, surface->output, &surface->cairo_to_pdf, - surface->font_subsets); + surface->font_subsets, + FALSE); _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, _cairo_pdf_surface_add_font, surface); @@ -769,14 +791,14 @@ _cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface, res->operators[op] = TRUE; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha, int *index) { int num_alphas, i; double other; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_group_resources_t *res = &surface->resources; num_alphas = _cairo_array_num_elements (&res->alphas); @@ -797,21 +819,21 @@ _cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_smask (cairo_pdf_surface_t *surface, cairo_pdf_resource_t smask) { return _cairo_array_append (&(surface->resources.smasks), &smask); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_pattern (cairo_pdf_surface_t *surface, cairo_pdf_resource_t pattern) { return _cairo_array_append (&(surface->resources.patterns), &pattern); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_shading (cairo_pdf_surface_t *surface, cairo_pdf_resource_t shading) { @@ -819,14 +841,14 @@ _cairo_pdf_surface_add_shading (cairo_pdf_surface_t *surface, } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_xobject (cairo_pdf_surface_t *surface, cairo_pdf_resource_t xobject) { return _cairo_array_append (&(surface->resources.xobjects), &xobject); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_font (unsigned int font_id, unsigned int subset_id, void *closure) @@ -834,7 +856,7 @@ _cairo_pdf_surface_add_font (unsigned int font_id, cairo_pdf_surface_t *surface = closure; cairo_pdf_font_t font; int num_fonts, i; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_group_resources_t *res = &surface->resources; num_fonts = _cairo_array_num_elements (&res->fonts); @@ -1093,7 +1115,7 @@ _cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group) free (group); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) { @@ -1187,6 +1209,20 @@ _cairo_pdf_surface_release_source_image_from_pattern (cairo_pdf_surface_t } static cairo_int_status_t +_get_jbig2_image_info (cairo_surface_t *source, + cairo_image_info_t *info, + const unsigned char **mime_data, + unsigned long *mime_data_length) +{ + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2, + mime_data, mime_data_length); + if (*mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return _cairo_image_info_get_jbig2_info (info, *mime_data, *mime_data_length); +} + +static cairo_int_status_t _get_jpx_image_info (cairo_surface_t *source, cairo_image_info_t *info, const unsigned char **mime_data, @@ -1263,6 +1299,15 @@ _get_source_surface_size (cairo_surface_t *source, extents->x = 0; extents->y = 0; + status = _get_jbig2_image_info (source, &info, &mime_data, &mime_data_length); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *width = info.width; + *height = info.height; + extents->width = info.width; + extents->height = info.height; + return status; + } + status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { *width = info.width; @@ -1295,9 +1340,12 @@ _get_source_surface_size (cairo_surface_t *source, * @surface: the pdf surface * @source_surface: A #cairo_surface_t to use as the source surface * @source_pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source + * @op: the operator used to composite this source * @filter: filter type of the source pattern * @stencil_mask: if true, the surface will be written to the PDF as an /ImageMask + * @smask: if true, only the alpha channel will be written (images only) * @extents: extents of the operation that is using this source + * @smask_res: if not NULL, the image written will specify this resource as the smask for the image (images only) * @surface_res: return PDF resource number of the surface * @width: returns width of surface * @height: returns height of surface @@ -1315,13 +1363,16 @@ _get_source_surface_size (cairo_surface_t *source, * Only one of @source_pattern or @source_surface is to be * specified. Set the other to NULL. **/ -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, cairo_surface_t *source_surface, const cairo_pattern_t *source_pattern, + cairo_operator_t op, cairo_filter_t filter, cairo_bool_t stencil_mask, + cairo_bool_t smask, const cairo_rectangle_int_t *extents, + cairo_pdf_resource_t *smask_res, cairo_pdf_resource_t *surface_res, int *width, int *height, @@ -1332,9 +1383,9 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, 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_int_status_t status; cairo_bool_t interpolate; - unsigned char *unique_id; + unsigned char *unique_id = NULL; unsigned long unique_id_length = 0; cairo_image_surface_t *image; void *image_extra; @@ -1419,13 +1470,19 @@ release_source: } surface_entry->id = surface_key.id; + surface_entry->operator = op; surface_entry->interpolate = interpolate; surface_entry->stencil_mask = stencil_mask; + surface_entry->smask = smask; surface_entry->unique_id_length = unique_id_length; surface_entry->unique_id = unique_id; surface_entry->width = *width; surface_entry->height = *height; surface_entry->extents = *source_extents; + if (smask_res) + surface_entry->smask_res = *smask_res; + else + surface_entry->smask_res.id = 0; _cairo_pdf_source_surface_init_key (surface_entry); src_surface.hash_entry = surface_entry; @@ -1476,24 +1533,26 @@ fail1: return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, + cairo_operator_t op, const cairo_rectangle_int_t *extents, cairo_bool_t is_shading, cairo_pdf_resource_t *pattern_res, cairo_pdf_resource_t *gstate_res) { cairo_pdf_pattern_t pdf_pattern; - cairo_status_t status; + cairo_int_status_t status; pdf_pattern.is_shading = is_shading; + pdf_pattern.operator = op; /* Solid colors are emitted into the content stream */ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { pattern_res->id = 0; gstate_res->id = 0; - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern); @@ -1545,7 +1604,7 @@ _cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t *surface, return status; } - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } /* Get BBox in PDF coordinates from extents in cairo coordinates */ @@ -1560,37 +1619,41 @@ _get_bbox_from_extents (double surface_height, bbox->p2.y = surface_height - extents->y; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_pdf_shading (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, + cairo_operator_t op, const cairo_rectangle_int_t *extents, cairo_pdf_resource_t *shading_res, cairo_pdf_resource_t *gstate_res) { return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface, pattern, + op, extents, TRUE, shading_res, gstate_res); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, + cairo_operator_t op, const cairo_rectangle_int_t *extents, cairo_pdf_resource_t *pattern_res, cairo_pdf_resource_t *gstate_res) { return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface, pattern, + op, extents, FALSE, pattern_res, gstate_res); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *resource, cairo_bool_t compressed, @@ -1659,22 +1722,22 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) { - cairo_status_t status; + cairo_int_status_t status; long length; if (! surface->pdf_stream.active) - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (surface->pdf_stream.compressed) { - cairo_status_t status2; + cairo_int_status_t status2; status2 = _cairo_output_stream_destroy (surface->output); - if (likely (status == CAIRO_STATUS_SUCCESS)) + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = status2; surface->output = surface->pdf_stream.old_output; @@ -1700,7 +1763,7 @@ _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) surface->pdf_stream.active = FALSE; - if (likely (status == CAIRO_STATUS_SUCCESS)) + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = _cairo_output_stream_get_status (surface->output); return status; @@ -1755,12 +1818,12 @@ _cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, "endobj\n"); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, const cairo_box_double_t *bbox, cairo_pdf_resource_t *resource) { - cairo_status_t status; + cairo_int_status_t status; assert (surface->pdf_stream.active == FALSE); assert (surface->group_stream.active == FALSE); @@ -1798,11 +1861,11 @@ _cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface, const cairo_box_double_t *bbox) { - cairo_status_t status; + cairo_int_status_t status; status = _cairo_pdf_surface_open_group (surface, bbox, NULL); if (unlikely (status)) @@ -1810,14 +1873,14 @@ _cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface, surface->group_stream.is_knockout = TRUE; - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *group) { - cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS, status2; assert (surface->pdf_stream.active == FALSE); assert (surface->group_stream.active == TRUE); @@ -1846,7 +1909,7 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, *group = surface->group_stream.resource; status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; surface->group_stream.mem_stream = NULL; @@ -1855,13 +1918,14 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, const cairo_box_double_t *bbox, cairo_pdf_resource_t *resource, - cairo_bool_t is_form) + cairo_bool_t is_form, + cairo_bool_t is_group) { - cairo_status_t status; + cairo_int_status_t status; assert (surface->pdf_stream.active == FALSE); assert (surface->group_stream.active == FALSE); @@ -1873,25 +1937,41 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, if (is_form) { assert (bbox != NULL); - status = - _cairo_pdf_surface_open_stream (surface, - resource, - surface->compress_content, - " /Type /XObject\n" - " /Subtype /Form\n" - " /BBox [ %f %f %f %f ]\n" - " /Group <<\n" - " /Type /Group\n" - " /S /Transparency\n" - " /I true\n" - " /CS /DeviceRGB\n" - " >>\n" - " /Resources %d 0 R\n", - bbox->p1.x, - bbox->p1.y, - bbox->p2.x, - bbox->p2.y, - surface->content_resources.id); + if (is_group) { + status = + _cairo_pdf_surface_open_stream (surface, + resource, + surface->compress_content, + " /Type /XObject\n" + " /Subtype /Form\n" + " /BBox [ %f %f %f %f ]\n" + " /Group <<\n" + " /Type /Group\n" + " /S /Transparency\n" + " /I true\n" + " /CS /DeviceRGB\n" + " >>\n" + " /Resources %d 0 R\n", + bbox->p1.x, + bbox->p1.y, + bbox->p2.x, + bbox->p2.y, + surface->content_resources.id); + } else { + status = + _cairo_pdf_surface_open_stream (surface, + resource, + surface->compress_content, + " /Type /XObject\n" + " /Subtype /Form\n" + " /BBox [ %f %f %f %f ]\n" + " /Resources %d 0 R\n", + bbox->p1.x, + bbox->p1.y, + bbox->p2.x, + bbox->p2.y, + surface->content_resources.id); + } } else { status = _cairo_pdf_surface_open_stream (surface, @@ -1909,10 +1989,10 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface) { - cairo_status_t status; + cairo_int_status_t status; assert (surface->pdf_stream.active == TRUE); assert (surface->group_stream.active == FALSE); @@ -1956,6 +2036,8 @@ _cairo_pdf_surface_finish (void *abstract_surface) long offset; cairo_pdf_resource_t info, catalog; cairo_status_t status, status2; + int size, i; + cairo_pdf_jbig2_global_t *global; status = surface->base.status; if (status == CAIRO_STATUS_SUCCESS) @@ -2045,6 +2127,17 @@ _cairo_pdf_surface_finish (void *abstract_surface) surface->font_subsets = NULL; } + size = _cairo_array_num_elements (&surface->jbig2_global); + for (i = 0; i < size; i++) { + global = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, i); + free(global->id); + if (!global->emitted) + return _cairo_error (CAIRO_STATUS_JBIG2_GLOBAL_MISSING); + } + _cairo_array_fini (&surface->jbig2_global); + + _cairo_array_truncate (&surface->page_surfaces, 0); + _cairo_surface_clipper_reset (&surface->clipper); return status; @@ -2085,7 +2178,7 @@ static cairo_int_status_t _cairo_pdf_surface_has_fallback_images (void *abstract_surface, cairo_bool_t has_fallbacks) { - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_surface_t *surface = abstract_surface; cairo_box_double_t bbox; @@ -2094,7 +2187,7 @@ _cairo_pdf_surface_has_fallback_images (void *abstract_surface, bbox.p1.y = 0; bbox.p2.x = surface->width; bbox.p2.y = surface->height; - status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks); + status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks, has_fallbacks); if (unlikely (status)) return status; @@ -2107,7 +2200,7 @@ _cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface) return TRUE; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surface, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents, @@ -2144,7 +2237,7 @@ _cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surfa h = image->height; if (_cairo_fixed_integer_ceil(box.p1.x) < 0 || _cairo_fixed_integer_ceil(box.p1.y) < 0 || - _cairo_fixed_integer_floor(box.p2.y) > w || + _cairo_fixed_integer_floor(box.p2.x) > w || _cairo_fixed_integer_floor(box.p2.y) > h) { pad_image = _cairo_image_surface_create_with_content (image->base.content, @@ -2169,9 +2262,12 @@ _cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surfa status = _cairo_pdf_surface_add_source_surface (surface, pad_image, NULL, + FALSE, source->filter, FALSE, + FALSE, extents, + NULL, surface_res, width, height, @@ -2202,24 +2298,16 @@ BAIL: return status; } -/* Emit alpha channel from the image into the given data, providing - * an id that can be used to reference the resulting SMask object. - * - * In the case that the alpha channel happens to be all opaque, then - * no SMask object will be emitted and *id_ret will be set to 0. - * - * When stencil_mask is TRUE, stream_res is an an input specifying the - * resource to use. When stencil_mask is FALSE, a new resource will be - * created and returned in stream_res. +/* Emit alpha channel from the image into stream_res. */ -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, cairo_image_surface_t *image, cairo_bool_t stencil_mask, - const char *interpolate, + cairo_bool_t interpolate, cairo_pdf_resource_t *stream_res) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; char *alpha; unsigned long alpha_size; uint32_t *pixel32; @@ -2229,6 +2317,7 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, /* This is the only image format we support, which simplifies things. */ assert (image->format == CAIRO_FORMAT_ARGB32 || + image->format == CAIRO_FORMAT_RGB24 || image->format == CAIRO_FORMAT_A8 || image->format == CAIRO_FORMAT_A1 ); @@ -2237,11 +2326,10 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, assert (transparency == CAIRO_IMAGE_IS_OPAQUE || transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA); } else { - if (transparency == CAIRO_IMAGE_IS_OPAQUE) - return status; + assert (transparency != CAIRO_IMAGE_IS_OPAQUE); } - if (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA) { + if (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA || transparency == CAIRO_IMAGE_IS_OPAQUE) { alpha_size = (image->width + 7) / 8 * image->height; alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height); } else { @@ -2256,7 +2344,10 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, i = 0; for (y = 0; y < image->height; y++) { - if (image->format == CAIRO_FORMAT_A1) { + if (transparency == CAIRO_IMAGE_IS_OPAQUE) { + for (x = 0; x < (image->width + 7) / 8; x++) + alpha[i++] = 0xff; + } else if (image->format == CAIRO_FORMAT_A1) { pixel8 = (uint8_t *) (image->data + y * image->stride); for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) { @@ -2308,11 +2399,11 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, " /Interpolate %s\n" " /BitsPerComponent 1\n" " /Decode [1 0]\n", - image->width, image->height, interpolate); + image->width, image->height, + interpolate ? "true" : "false"); } else { - stream_res->id = 0; status = _cairo_pdf_surface_open_stream (surface, - NULL, + stream_res, TRUE, " /Type /XObject\n" " /Subtype /Image\n" @@ -2321,15 +2412,13 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, " /ColorSpace /DeviceGray\n" " /Interpolate %s\n" " /BitsPerComponent %d\n", - image->width, image->height, interpolate, + image->width, image->height, + interpolate ? "true" : "false", transparency == CAIRO_IMAGE_HAS_ALPHA ? 8 : 1); } if (unlikely (status)) goto CLEANUP_ALPHA; - if (!stencil_mask) - *stream_res = surface->pdf_stream.self; - _cairo_output_stream_write (surface->output, alpha, alpha_size); status = _cairo_pdf_surface_close_stream (surface); @@ -2339,25 +2428,33 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, return status; } -/* Emit image data into the given surface, providing a resource that - * can be used to reference the data in image_ret. */ -static cairo_status_t -_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, - cairo_image_surface_t *image_surf, - cairo_pdf_resource_t *image_res, - cairo_filter_t filter, - cairo_bool_t stencil_mask) +/** + * _cairo_pdf_surface_emit_image: + * @surface: the pdf surface + * @image_surf: The image to write + * @surface_entry: Contains image resource, smask resource, interpolate and stencil mask parameters. + * + * Emit an image stream using the @image_res resource and write out + * the image data from @image_surf. If @smask_res is not null, @smask_res will + * be specified as the smask for the image. Otherwise emit the an smask if + * the image is requires one. + **/ +static cairo_int_status_t +_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, + cairo_image_surface_t *image_surf, + cairo_pdf_source_surface_entry_t *surface_entry) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; char *data; unsigned long data_size; uint32_t *pixel; int i, x, y, bit; cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */ cairo_bool_t need_smask; - const char *interpolate = "true"; cairo_image_color_t color; cairo_image_surface_t *image; + cairo_image_transparency_t transparency; + char smask_buf[30]; image = image_surf; if (image->format != CAIRO_FORMAT_RGB24 && @@ -2386,26 +2483,19 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, goto CLEANUP; } - switch (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; + if (surface_entry->smask || surface_entry->stencil_mask) { + return _cairo_pdf_surface_emit_smask (surface, image, + surface_entry->stencil_mask, + surface_entry->interpolate, + &surface_entry->surface_res); } - if (stencil_mask) - return _cairo_pdf_surface_emit_smask (surface, image, stencil_mask, interpolate, image_res); - color = _cairo_image_analyze_color (image); switch (color) { - case CAIRO_IMAGE_IS_COLOR: + default: case CAIRO_IMAGE_UNKNOWN_COLOR: + ASSERT_NOT_REACHED; + case CAIRO_IMAGE_IS_COLOR: data_size = image->height * image->width * 3; data = _cairo_malloc_abc (image->width, image->height, 3); break; @@ -2484,46 +2574,53 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, i++; } - need_smask = FALSE; - if (image->format == CAIRO_FORMAT_ARGB32 || - image->format == CAIRO_FORMAT_A8 || - image->format == CAIRO_FORMAT_A1) { - status = _cairo_pdf_surface_emit_smask (surface, image, FALSE, interpolate, &smask); - if (unlikely (status)) - goto CLEANUP_RGB; + if (surface_entry->smask_res.id != 0) { + need_smask = TRUE; + smask = surface_entry->smask_res; + } else { + need_smask = FALSE; + if (image->format == CAIRO_FORMAT_ARGB32 || + image->format == CAIRO_FORMAT_A8 || + image->format == CAIRO_FORMAT_A1) + { + transparency = _cairo_image_analyze_transparency (image); + if (transparency != CAIRO_IMAGE_IS_OPAQUE) { + need_smask = TRUE; + smask = _cairo_pdf_surface_new_object (surface); + if (smask.id == 0) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_RGB; + } - if (smask.id) - need_smask = TRUE; + status = _cairo_pdf_surface_emit_smask (surface, image, FALSE, surface_entry->interpolate, &smask); + if (unlikely (status)) + goto CLEANUP_RGB; + } + } } -#define IMAGE_DICTIONARY " /Type /XObject\n" \ - " /Subtype /Image\n" \ - " /Width %d\n" \ - " /Height %d\n" \ - " /ColorSpace %s\n" \ - " /Interpolate %s\n" \ - " /BitsPerComponent %d\n" - if (need_smask) - status = _cairo_pdf_surface_open_stream (surface, - image_res, - TRUE, - IMAGE_DICTIONARY - " /SMask %d 0 R\n", - image->width, image->height, - color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray", - interpolate, - color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8, - smask.id); + snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", smask.id); else - status = _cairo_pdf_surface_open_stream (surface, - image_res, - TRUE, - IMAGE_DICTIONARY, - image->width, image->height, - color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray", - interpolate, - color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8); + smask_buf[0] = 0; + + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + TRUE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace %s\n" + " /Interpolate %s\n" + " /BitsPerComponent %d\n" + "%s", + image->width, + image->height, + color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray", + surface_entry->interpolate ? "true" : "false", + color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8, + smask_buf); if (unlikely (status)) goto CLEANUP_RGB; @@ -2542,14 +2639,165 @@ 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) +_cairo_pdf_surface_lookup_jbig2_global (cairo_pdf_surface_t *surface, + const unsigned char *global_id, + unsigned long global_id_length, + cairo_pdf_jbig2_global_t **entry) { - cairo_status_t status; + cairo_pdf_jbig2_global_t global; + int size, i; + cairo_int_status_t status; + + size = _cairo_array_num_elements (&surface->jbig2_global); + for (i = 0; i < size; i++) { + *entry = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, i); + if ((*entry)->id && global_id && (*entry)->id_length == global_id_length + && memcmp((*entry)->id, global_id, global_id_length) == 0) { + return CAIRO_STATUS_SUCCESS; + } + } + + global.id = malloc(global_id_length); + if (unlikely (global.id == NULL)) { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memcpy (global.id, global_id, global_id_length); + global.id_length = global_id_length; + global.res = _cairo_pdf_surface_new_object (surface); + if (global.res.id == 0) { + free(global.id); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + global.emitted = FALSE; + status = _cairo_array_append (&surface->jbig2_global, &global); + if (unlikely(status)) + return status; + + size = _cairo_array_num_elements (&surface->jbig2_global); + *entry = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, size - 1); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_source_surface_entry_t *surface_entry) +{ + cairo_int_status_t status; const unsigned char *mime_data; unsigned long mime_data_length; cairo_image_info_t info; + const unsigned char *global_id; + unsigned long global_id_length; + const unsigned char *global_data; + unsigned long global_data_length; + cairo_pdf_jbig2_global_t *global_entry = NULL; /* hide compiler warning */ + char smask_buf[30]; + char decode_parms_buf[100]; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jbig2_info (&info, mime_data, mime_data_length); + if (status) + return status; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, + &global_id, &global_id_length); + if (global_id && global_id_length > 0) { + status = _cairo_pdf_surface_lookup_jbig2_global (surface, global_id, global_id_length, &global_entry); + if (unlikely(status)) + return status; + + if (!global_entry->emitted) { + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL, + &global_data, &global_data_length); + if (global_data) { + status = _cairo_pdf_surface_open_stream (surface, &global_entry->res, FALSE, NULL); + if (unlikely(status)) + return status; + + _cairo_output_stream_write (surface->output, global_data, global_data_length); + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely(status)) + return status; + + global_entry->emitted = TRUE; + } + } + + snprintf(decode_parms_buf, sizeof(decode_parms_buf), + " /DecodeParms << /JBIG2Globals %d 0 R >>\n", global_entry->res.id); + } else { + decode_parms_buf[0] = 0; + } + + if (surface_entry->smask_res.id) + snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", surface_entry->smask_res.id); + else + smask_buf[0] = 0; + + if (surface_entry->stencil_mask) { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /ImageMask true\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [1 0]\n" + " /Filter /JPXDecode\n" + "%s", + info.width, + info.height, + surface_entry->interpolate ? "true" : "false", + decode_parms_buf); + } else { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace /DeviceGray\n" + " /BitsPerComponent 1\n" + " /Interpolate %s\n" + "%s" + " /Filter /JBIG2Decode\n" + "%s", + info.width, + info.height, + surface_entry->interpolate ? "true" : "false", + smask_buf, + decode_parms_buf); + } + if (unlikely(status)) + return status; + + _cairo_output_stream_write (surface->output, mime_data, mime_data_length); + status = _cairo_pdf_surface_close_stream (surface); + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_source_surface_entry_t *surface_entry) +{ + cairo_int_status_t status; + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_image_info_t info; + char smask_buf[30]; if (surface->pdf_version < CAIRO_PDF_VERSION_1_5) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -2563,16 +2811,49 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, if (status) return status; - status = _cairo_pdf_surface_open_stream (surface, - &res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /Filter /JPXDecode\n", - info.width, - info.height); + if ((surface_entry->smask || surface_entry->stencil_mask) && info.num_components != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface_entry->stencil_mask) && info.bits_per_component != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface_entry->smask_res.id) + snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", surface_entry->smask_res.id); + else + smask_buf[0] = 0; + + if (surface_entry->stencil_mask) { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /ImageMask true\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [1 0]\n" + " /Filter /JPXDecode\n", + info.width, + info.height, + surface_entry->interpolate ? "true" : "false"); + } else { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + "%s" + " /Filter /JPXDecode\n", + info.width, + info.height, + surface_entry->interpolate ? "true" : "false", + smask_buf); + } if (status) return status; @@ -2583,15 +2864,16 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, } 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) +_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_source_surface_entry_t *surface_entry) { - cairo_status_t status; + cairo_int_status_t status; const unsigned char *mime_data; unsigned long mime_data_length; cairo_image_info_t info; const char *colorspace; + char smask_buf[30]; cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); @@ -2604,6 +2886,12 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; + if ((surface_entry->smask || surface_entry->stencil_mask) && info.num_components != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface_entry->stencil_mask) && info.bits_per_component != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + switch (info.num_components) { case 1: colorspace = "/DeviceGray"; @@ -2618,20 +2906,47 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - status = _cairo_pdf_surface_open_stream (surface, - &res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /ColorSpace %s\n" - " /BitsPerComponent %d\n" - " /Filter /DCTDecode\n", - info.width, - info.height, - colorspace, - info.bits_per_component); + if (surface_entry->smask_res.id) + snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", surface_entry->smask_res.id); + else + smask_buf[0] = 0; + + if (surface_entry->stencil_mask) { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /ImageMask true\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [1 0]\n" + " /Filter /DCTDecode\n", + info.width, + info.height, + surface_entry->interpolate ? "true" : "false"); + } else { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace %s\n" + " /Interpolate %s\n" + " /BitsPerComponent %d\n" + "%s" + " /Filter /DCTDecode\n", + info.width, + info.height, + colorspace, + surface_entry->interpolate ? "true" : "false", + info.bits_per_component, + smask_buf); + } if (unlikely (status)) return status; @@ -2641,7 +2956,7 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, cairo_pdf_source_surface_t *source) { @@ -2650,6 +2965,20 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, cairo_int_status_t status; if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + status = _cairo_pdf_surface_emit_jbig2_image (surface, source->surface, source->hash_entry); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_pdf_surface_emit_jpx_image (surface, source->surface, source->hash_entry); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_pdf_surface_emit_jpeg_image (surface, source->surface, source->hash_entry); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { status = _cairo_surface_acquire_source_image (source->surface, &image, &image_extra); } else { status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source->raster_pattern, @@ -2658,22 +2987,10 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; - if (!source->hash_entry->stencil_mask) { - status = _cairo_pdf_surface_emit_jpx_image (surface, &image->base, source->hash_entry->surface_res); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto release_source; + status = _cairo_pdf_surface_emit_image (surface, + image, + source->hash_entry); - status = _cairo_pdf_surface_emit_jpeg_image (surface, &image->base, source->hash_entry->surface_res); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto release_source; - } - - status = _cairo_pdf_surface_emit_image (surface, image, - &source->hash_entry->surface_res, - source->hash_entry->interpolate, - source->hash_entry->stencil_mask); - -release_source: if (source->type == CAIRO_PATTERN_TYPE_SURFACE) _cairo_surface_release_source_image (source->surface, image, image_extra); else @@ -2683,7 +3000,7 @@ release_source: return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, cairo_pdf_source_surface_t *pdf_source) { @@ -2699,6 +3016,8 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, int width; int height; cairo_bool_t is_subsurface; + cairo_bool_t transparency_group; + cairo_recording_surface_t *recording; assert (pdf_source->type == CAIRO_PATTERN_TYPE_SURFACE); extents = &pdf_source->hash_entry->extents; @@ -2718,6 +3037,9 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, is_subsurface = TRUE; } + assert (source->type == CAIRO_SURFACE_TYPE_RECORDING); + recording = (cairo_recording_surface_t *) source; + old_width = surface->width; old_height = surface->height; old_paginated_mode = surface->paginated_mode; @@ -2734,7 +3056,16 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; _cairo_pdf_group_resources_clear (&surface->resources); _get_bbox_from_extents (height, extents, &bbox); - status = _cairo_pdf_surface_open_content_stream (surface, &bbox, &pdf_source->hash_entry->surface_res, TRUE); + + /* We can optimize away the transparency group allowing the viewer + * to replay the group in place when all operators are OVER and the + * recording contains only opaque and/or clear alpha. + */ + transparency_group = !(pdf_source->hash_entry->operator == CAIRO_OPERATOR_OVER && + _cairo_recording_surface_has_only_bilevel_alpha (recording) && + _cairo_recording_surface_has_only_op_over (recording)); + status = _cairo_pdf_surface_open_content_stream (surface, &bbox, &pdf_source->hash_entry->surface_res, + TRUE, transparency_group); if (unlikely (status)) goto err; @@ -2772,7 +3103,7 @@ err: return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, cairo_pdf_source_surface_t *src_surface) { @@ -2783,12 +3114,12 @@ _cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, return _cairo_pdf_surface_emit_image_surface (surface, src_surface); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { cairo_pattern_t *pattern = pdf_pattern->pattern; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_resource_t pattern_resource = {0}; cairo_matrix_t cairo_p2d, pdf_p2d; cairo_extend_t extend = cairo_pattern_get_extend (pattern); @@ -2818,9 +3149,12 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_add_source_surface (surface, NULL, pattern, + pdf_pattern->operator, pattern->filter, FALSE, + FALSE, &pdf_pattern->extents, + NULL, &pattern_resource, &pattern_width, &pattern_height, @@ -2911,7 +3245,7 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, cairo_p2d = pattern->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf); cairo_matrix_translate (&pdf_p2d, -x_offset, -y_offset); @@ -2989,7 +3323,7 @@ typedef struct _cairo_pdf_color_stop { cairo_pdf_resource_t resource; } cairo_pdf_color_stop_t; -static cairo_status_t +static cairo_int_status_t cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface, cairo_pdf_color_stop_t *stop1, cairo_pdf_color_stop_t *stop2, @@ -2998,7 +3332,7 @@ cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface, int num_elems, i; cairo_pdf_rgb_linear_function_t elem; cairo_pdf_resource_t res; - cairo_status_t status; + cairo_int_status_t status; num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions); for (i = 0; i < num_elems; i++) { @@ -3042,7 +3376,7 @@ cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface, cairo_pdf_color_stop_t *stop1, cairo_pdf_color_stop_t *stop2, @@ -3051,7 +3385,7 @@ cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface, int num_elems, i; cairo_pdf_alpha_linear_function_t elem; cairo_pdf_resource_t res; - cairo_status_t status; + cairo_int_status_t status; num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions); for (i = 0; i < num_elems; i++) { @@ -3091,7 +3425,7 @@ cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t *surface, unsigned int n_stops, cairo_pdf_color_stop_t *stops, @@ -3100,7 +3434,7 @@ _cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t *surface, { cairo_pdf_resource_t res; unsigned int i; - cairo_status_t status; + cairo_int_status_t status; /* emit linear gradients between pairs of subsequent stops... */ for (i = 0; i < n_stops-1; i++) { @@ -3182,7 +3516,7 @@ calc_gradient_color (cairo_pdf_color_stop_t *new_stop, #define COLOR_STOP_EPSILON 1e-6 -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern, cairo_pdf_resource_t *color_function, @@ -3192,7 +3526,7 @@ _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, unsigned int n_stops; unsigned int i; cairo_bool_t emit_alpha = FALSE; - cairo_status_t status; + cairo_int_status_t status; color_function->id = 0; alpha_function->id = 0; @@ -3326,7 +3660,7 @@ BAIL: return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern, cairo_pdf_resource_t *function, @@ -3387,14 +3721,14 @@ _cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern, cairo_pdf_resource_t gstate_resource, cairo_pdf_resource_t gradient_mask) { cairo_pdf_resource_t smask_resource; - cairo_status_t status; + cairo_int_status_t status; char buf[100]; double x1, y1, x2, y2; @@ -3531,11 +3865,11 @@ _cairo_pdf_surface_output_gradient (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, "<< /Type /Pattern\n" " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n", - pat_to_pdf->xx, pat_to_pdf->yx, - pat_to_pdf->xy, pat_to_pdf->yy, - pat_to_pdf->x0, pat_to_pdf->y0); + " /Matrix [ "); + _cairo_output_stream_print_matrix (surface->output, pat_to_pdf); + _cairo_output_stream_printf (surface->output, + " ]\n" + " /Shading\n"); } if (pdf_pattern->pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { @@ -3577,12 +3911,14 @@ _cairo_pdf_surface_output_gradient (cairo_pdf_surface_t *surface, if (!pdf_pattern->is_shading) { _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); + ">>\n"); } + + _cairo_output_stream_printf (surface->output, + "endobj\n"); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { @@ -3591,7 +3927,7 @@ _cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface, cairo_matrix_t pat_to_pdf; cairo_circle_double_t start, end; double domain[2]; - cairo_status_t status; + cairo_int_status_t status; assert (pattern->n_stops != 0); @@ -3605,7 +3941,7 @@ _cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface, pat_to_pdf = pattern->base.matrix; status = cairo_matrix_invert (&pat_to_pdf); /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); if (pattern->base.extend == CAIRO_EXTEND_REPEAT || @@ -3728,12 +4064,12 @@ _cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { cairo_matrix_t pat_to_pdf; - cairo_status_t status; + cairo_int_status_t status; cairo_pattern_t *pattern = pdf_pattern->pattern; cairo_pdf_shading_t shading; int i; @@ -3742,7 +4078,7 @@ _cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, pat_to_pdf = pattern->matrix; status = cairo_matrix_invert (&pat_to_pdf); /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); @@ -3791,14 +4127,14 @@ _cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, "%d 0 obj\n" "<< /Type /Pattern\n" " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" + " /Matrix [ ", + pdf_pattern->pattern_res.id); + _cairo_output_stream_print_matrix (surface->output, &pat_to_pdf); + _cairo_output_stream_printf (surface->output, + " ]\n" " /Shading %d 0 R\n" ">>\n" "endobj\n", - 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, res.id); if (pdf_pattern->gstate_res.id != 0) { @@ -3852,14 +4188,14 @@ _cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, "%d 0 obj\n" "<< /Type /Pattern\n" " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" + " /Matrix [ ", + mask_resource.id); + _cairo_output_stream_print_matrix (surface->output, &pat_to_pdf); + _cairo_output_stream_printf (surface->output, + " ]\n" " /Shading %d 0 R\n" ">>\n" "endobj\n", - mask_resource.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, res.id); status = cairo_pdf_surface_emit_transparency_group (surface, @@ -3873,11 +4209,11 @@ _cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { double old_width, old_height; - cairo_status_t status; + cairo_int_status_t status; old_width = surface->width; old_height = surface->height; @@ -3918,16 +4254,18 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, + cairo_operator_t op, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents, + cairo_pdf_resource_t *smask_res, cairo_bool_t stencil_mask) { cairo_pdf_resource_t surface_res; int width, height; cairo_matrix_t cairo_p2d, pdf_p2d; - cairo_status_t status; + cairo_int_status_t status; int alpha; cairo_rectangle_int_t extents2; double x_offset; @@ -3949,9 +4287,12 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_add_source_surface (surface, NULL, source, + op, source->filter, stencil_mask, + FALSE, extents, + smask_res, &surface_res, &width, &height, @@ -3965,7 +4306,7 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, cairo_p2d = source->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); pdf_p2d = surface->cairo_to_pdf; cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); @@ -3983,11 +4324,8 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, 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); + _cairo_output_stream_print_matrix (surface->output, &pdf_p2d); + _cairo_output_stream_printf (surface->output, " cm\n"); } status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); @@ -4008,28 +4346,29 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, return _cairo_pdf_surface_add_xobject (surface, surface_res); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_paint_gradient (cairo_pdf_surface_t *surface, + cairo_operator_t op, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents) { cairo_pdf_resource_t shading_res, gstate_res; cairo_matrix_t pat_to_pdf; - cairo_status_t status; + cairo_int_status_t status; int alpha; status = _cairo_pdf_surface_add_pdf_shading (surface, source, - extents, + op, extents, &shading_res, &gstate_res); if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; if (unlikely (status)) return status; pat_to_pdf = source->matrix; status = cairo_matrix_invert (&pat_to_pdf); /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); status = _cairo_pdf_operators_flush (&surface->pdf_operators); @@ -4037,11 +4376,8 @@ _cairo_pdf_surface_paint_gradient (cairo_pdf_surface_t *surface, return status; if (! _cairo_matrix_is_identity (&pat_to_pdf)) { - _cairo_output_stream_printf (surface->output, - "%f %f %f %f %f %f cm\n", - pat_to_pdf.xx, pat_to_pdf.yx, - pat_to_pdf.xy, pat_to_pdf.yy, - pat_to_pdf.x0, pat_to_pdf.y0); + _cairo_output_stream_print_matrix (surface->output, &pat_to_pdf); + _cairo_output_stream_printf (surface->output, " cm\n"); } status = _cairo_pdf_surface_add_shading (surface, shading_res); @@ -4071,8 +4407,9 @@ _cairo_pdf_surface_paint_gradient (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t *surface, + cairo_operator_t op, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents, cairo_bool_t mask) @@ -4081,13 +4418,16 @@ _cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t *surface, case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _cairo_pdf_surface_paint_surface_pattern (surface, + op, source, extents, + NULL, mask); case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: return _cairo_pdf_surface_paint_gradient (surface, + op, source, extents); @@ -4123,11 +4463,11 @@ _can_paint_pattern (const cairo_pattern_t *pattern) } } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, cairo_operator_t op) { - cairo_status_t status; + cairo_int_status_t status; if (op == surface->current_operator) return CAIRO_STATUS_SUCCESS; @@ -4144,13 +4484,13 @@ _cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, cairo_pdf_resource_t pattern_res, cairo_bool_t is_stroke) { - cairo_status_t status; + cairo_int_status_t status; int alpha; const cairo_color_t *solid_color = NULL; @@ -4366,14 +4706,14 @@ _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) "endobj\n"); } -static cairo_status_t +static cairo_int_status_t _utf8_to_pdf_string (const char *utf8, char **str_out) { int i; int len; cairo_bool_t ascii; char *str; - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; ascii = TRUE; len = strlen (utf8); @@ -4421,13 +4761,13 @@ _utf8_to_pdf_string (const char *utf8, char **str_out) return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface, const char *utf8) { uint16_t *utf16 = NULL; int utf16_len = 0; - cairo_status_t status; + cairo_int_status_t status; int i; if (utf8 && *utf8) { @@ -4651,7 +4991,7 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, #define PDF_UNITS_PER_EM 1000 -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset, cairo_cff_subset_t *subset) @@ -4660,7 +5000,7 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, cairo_pdf_resource_t subset_resource, to_unicode_stream; cairo_pdf_font_t font; unsigned int i, last_glyph; - cairo_status_t status; + cairo_int_status_t status; char tag[10]; _create_font_subset_tag (font_subset, subset->ps_name, tag); @@ -4690,7 +5030,7 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, &to_unicode_stream); - if (_cairo_status_is_error (status)) + if (_cairo_int_status_is_error (status)) return status; descriptor = _cairo_pdf_surface_new_object (surface); @@ -4848,11 +5188,11 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { - cairo_status_t status; + cairo_int_status_t status; cairo_cff_subset_t subset; char name[64]; @@ -4869,11 +5209,11 @@ _cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { - cairo_status_t status; + cairo_int_status_t status; cairo_cff_subset_t subset; char name[64]; @@ -4895,14 +5235,14 @@ _cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset, cairo_type1_subset_t *subset) { cairo_pdf_resource_t stream, descriptor, subset_resource, to_unicode_stream; cairo_pdf_font_t font; - cairo_status_t status; + cairo_int_status_t status; unsigned long length; unsigned int i, last_glyph; char tag[10]; @@ -4937,7 +5277,7 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, &to_unicode_stream); - if (_cairo_status_is_error (status)) + if (_cairo_int_status_is_error (status)) return status; last_glyph = font_subset->num_glyphs - 1; @@ -5037,11 +5377,11 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, return _cairo_array_append (&surface->fonts, &font); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { - cairo_status_t status; + cairo_int_status_t status; cairo_type1_subset_t subset; char name[64]; @@ -5061,11 +5401,11 @@ _cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { - cairo_status_t status; + cairo_int_status_t status; cairo_type1_subset_t subset; char name[64]; @@ -5085,13 +5425,13 @@ _cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_pdf_resource_t stream, descriptor, cidfont_dict; cairo_pdf_resource_t subset_resource, to_unicode_stream; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_font_t font; cairo_truetype_subset_t subset; unsigned int i, last_glyph; @@ -5131,7 +5471,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, &to_unicode_stream); - if (_cairo_status_is_error (status)) { + if (_cairo_int_status_is_error (status)) { _cairo_truetype_subset_fini (&subset); return status; } @@ -5298,7 +5638,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_emit_imagemask (cairo_image_surface_t *image, cairo_output_stream_t *stream) { @@ -5343,8 +5683,8 @@ _cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_su void *closure) { cairo_pdf_surface_t *surface = closure; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_status_t status2; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + cairo_int_status_t status2; unsigned int i; cairo_surface_t *type3_surface; cairo_output_stream_t *null_stream; @@ -5353,7 +5693,8 @@ _cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_su type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, null_stream, _cairo_pdf_emit_imagemask, - surface->font_subsets); + surface->font_subsets, + FALSE); if (unlikely (type3_surface->status)) { status2 = _cairo_output_stream_destroy (null_stream); status = type3_surface->status; @@ -5374,7 +5715,7 @@ _cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_su cairo_surface_destroy (type3_surface); status2 = _cairo_output_stream_destroy (null_stream); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; return status; @@ -5384,7 +5725,7 @@ static cairo_int_status_t _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_pdf_resource_t *glyphs, encoding, char_procs, subset_resource, to_unicode_stream; cairo_pdf_font_t font; double *widths; @@ -5416,7 +5757,8 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, NULL, _cairo_pdf_emit_imagemask, - surface->font_subsets); + surface->font_subsets, + FALSE); if (unlikely (type3_surface->status)) { free (glyphs); free (widths); @@ -5515,7 +5857,7 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, &to_unicode_stream); - if (_cairo_status_is_error (status)) { + if (_cairo_int_status_is_error (status)) { free (widths); return status; } @@ -5613,10 +5955,10 @@ _cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_sub return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) { - cairo_status_t status; + cairo_int_status_t status; status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, _cairo_pdf_surface_analyze_user_font_subset, @@ -5698,7 +6040,7 @@ _cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface) return offset; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) { @@ -5706,7 +6048,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, cairo_pdf_resource_t smask; cairo_pdf_smask_group_t *smask_group; cairo_pdf_resource_t pattern_res, gstate_res; - cairo_status_t status; + cairo_int_status_t status; cairo_box_double_t bbox; /* Create mask group */ @@ -5718,6 +6060,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, if (_can_paint_pattern (group->mask)) { _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_surface_paint_pattern (surface, + CAIRO_OPERATOR_OVER, group->mask, &group->extents, FALSE); @@ -5728,7 +6071,9 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, } else { pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, NULL, + status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, + CAIRO_OPERATOR_OVER, + NULL, &pattern_res, &gstate_res); if (unlikely (status)) return status; @@ -5791,6 +6136,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, if (_can_paint_pattern (group->source)) { _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_surface_paint_pattern (surface, + CAIRO_OPERATOR_OVER, group->source, &group->extents, FALSE); @@ -5801,7 +6147,9 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, } else { pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, NULL, + status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, + CAIRO_OPERATOR_OVER, + NULL, &pattern_res, &gstate_res); if (unlikely (status)) return status; @@ -5886,12 +6234,12 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) { double old_width, old_height; - cairo_status_t status; + cairo_int_status_t status; cairo_box_double_t bbox; old_width = surface->width; @@ -5965,14 +6313,14 @@ RESTORE_SIZE: return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface) { cairo_pdf_pattern_t pattern; cairo_pdf_smask_group_t *group; cairo_pdf_source_surface_t src_surface; unsigned int pattern_index, group_index, surface_index; - cairo_status_t status; + cairo_int_status_t status; /* Writing out PDF_MASK groups will cause additional smask groups * to be appended to surface->smask_groups. Additional patterns @@ -6013,11 +6361,11 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t page, knockout, res; - cairo_status_t status; + cairo_int_status_t status; unsigned int i, len; _cairo_pdf_group_resources_clear (&surface->resources); @@ -6056,7 +6404,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, NULL, NULL, FALSE); + status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE, FALSE); if (unlikely (status)) return status; @@ -6329,7 +6677,7 @@ static cairo_int_status_t _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) { cairo_box_double_t bbox; - cairo_status_t status; + cairo_int_status_t status; status = _cairo_pdf_surface_close_content_stream (surface); if (unlikely (status)) @@ -6344,17 +6692,194 @@ _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) bbox.p1.y = 0; bbox.p2.x = surface->width; bbox.p2.y = surface->height; - return _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE); + return _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE, TRUE); +} + +/* If source is an opaque image and mask is an image and both images + * have the same bounding box we can emit them as a image/smask pair. + */ +static cairo_int_status_t +_cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_rectangle_int_t *extents) +{ + cairo_int_status_t status; + cairo_image_surface_t *image; + void *image_extra; + cairo_image_transparency_t transparency; + cairo_pdf_resource_t smask_res; + int src_width, src_height; + int mask_width, mask_height; + double src_x_offset, src_y_offset; + double mask_x_offset, mask_y_offset; + double src_x1, src_y1, src_x2, src_y2; + double mask_x1, mask_y1, mask_x2, mask_y2; + cairo_matrix_t p2u; + double src_radius, mask_radius, e; + cairo_rectangle_int_t extents2; + cairo_bool_t need_smask; + + /* Check that source and mask are images */ + + if (!((source->type == CAIRO_PATTERN_TYPE_SURFACE || source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) && + (mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *) source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (source->extend != CAIRO_EXTEND_NONE || mask->extend != CAIRO_EXTEND_NONE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Check that source is opaque and get image sizes */ + + status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source, + &image, &image_extra); + if (unlikely (status)) + return status; + + if (image->base.status) + return image->base.status; + + src_width = image->width; + src_height = image->height; + if (source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + cairo_surface_get_device_offset (&image->base, &src_x_offset, &src_y_offset); + } else { + src_x_offset = 0; + src_y_offset = 0; + } + + transparency = _cairo_image_analyze_transparency (image); + _cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra); + + if (transparency != CAIRO_IMAGE_IS_OPAQUE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask, + &image, &image_extra); + if (unlikely (status)) + return status; + + if (image->base.status) + return image->base.status; + + mask_width = image->width; + mask_height = image->height; + if (mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + cairo_surface_get_device_offset (&image->base, &mask_x_offset, &mask_y_offset); + } else { + mask_x_offset = 0; + mask_y_offset = 0; + } + + transparency = _cairo_image_analyze_transparency (image); + need_smask = transparency != CAIRO_IMAGE_IS_OPAQUE; + + _cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra); + + /* Check that both images have the same extents with a tolerance + * of half the smallest source pixel. */ + + p2u = source->matrix; + status = cairo_matrix_invert (&p2u); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_INT_STATUS_SUCCESS); + src_x1 = 0; + src_y1 = 0; + src_x2 = src_width; + src_y2 = src_height; + cairo_matrix_transform_point (&p2u, &src_x1, &src_y1); + cairo_matrix_transform_point (&p2u, &src_x2, &src_y2); + src_radius = _cairo_matrix_transformed_circle_major_axis (&p2u, 0.5); + + p2u = mask->matrix; + status = cairo_matrix_invert (&p2u); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_INT_STATUS_SUCCESS); + mask_x1 = 0; + mask_y1 = 0; + mask_x2 = mask_width; + mask_y2 = mask_height; + cairo_matrix_transform_point (&p2u, &mask_x1, &mask_y1); + cairo_matrix_transform_point (&p2u, &mask_x2, &mask_y2); + mask_radius = _cairo_matrix_transformed_circle_major_axis (&p2u, 0.5); + + if (src_radius < mask_radius) + e = src_radius; + else + e = mask_radius; + + if (fabs(src_x1 - mask_x1) > e || + fabs(src_x2 - mask_x2) > e || + fabs(src_y1 - mask_y1) > e || + fabs(src_y2 - mask_y2) > e) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Check both images have same device offset */ + if (fabs(src_x_offset - mask_x_offset) > e || + fabs(src_y_offset - mask_y_offset) > e) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (need_smask) { + status = _cairo_pdf_surface_add_source_surface (surface, + NULL, + mask, + op, + source->filter, + FALSE, + TRUE, + extents, + NULL, + &smask_res, + &mask_width, + &mask_height, + &mask_x_offset, + &mask_y_offset, + &extents2); + 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\n"); + status = _cairo_pdf_surface_paint_surface_pattern (surface, op, source, extents, + need_smask ? &smask_res : NULL, + FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + + status = _cairo_output_stream_get_status (surface->output); + + + return status; } /* A PDF stencil mask is an A1 mask used with the current color */ static cairo_int_status_t _cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t *surface, + cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_rectangle_int_t *extents) { - cairo_status_t status; + cairo_int_status_t status; cairo_image_surface_t *image; void *image_extra; cairo_image_transparency_t transparency; @@ -6396,7 +6921,7 @@ _cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t *surface, return status; _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_surface_pattern (surface, mask, NULL, TRUE); + status = _cairo_pdf_surface_paint_surface_pattern (surface, op, mask, extents, NULL, TRUE); if (unlikely (status)) return status; @@ -6472,6 +6997,7 @@ _cairo_pdf_surface_paint (void *abstract_surface, if (_can_paint_pattern (source)) { _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_surface_paint_pattern (surface, + op, source, &extents.bounded, FALSE); @@ -6485,7 +7011,7 @@ _cairo_pdf_surface_paint (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) @@ -6567,7 +7093,7 @@ _cairo_pdf_surface_mask (void *abstract_surface, return status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - cairo_status_t source_status, mask_status; + cairo_int_status_t source_status, mask_status; status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); if (_cairo_int_status_is_error (status)) @@ -6625,8 +7151,13 @@ _cairo_pdf_surface_mask (void *abstract_surface, if (unlikely (status)) goto cleanup; + /* Check if we can combine source and mask into a smask image */ + status = _cairo_pdf_surface_emit_combined_smask (surface, op, source, mask, &extents.bounded); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto cleanup; + /* Check if we can use a stencil mask */ - status = _cairo_pdf_surface_emit_stencil_mask (surface, source, mask, &extents.bounded); + status = _cairo_pdf_surface_emit_stencil_mask (surface, op, source, mask, &extents.bounded); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto cleanup; @@ -6743,7 +7274,7 @@ _cairo_pdf_surface_stroke (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) @@ -6896,6 +7427,7 @@ _cairo_pdf_surface_fill (void *abstract_surface, goto cleanup; status = _cairo_pdf_surface_paint_pattern (surface, + op, source, &extents.bounded, FALSE); @@ -6909,7 +7441,7 @@ _cairo_pdf_surface_fill (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) @@ -7084,6 +7616,7 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, fill_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, + fill_op, &extents.bounded, &fill_pattern_res, &gstate_res); @@ -7096,6 +7629,7 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, stroke_source, + stroke_op, &extents.bounded, &stroke_pattern_res, &gstate_res); @@ -7187,7 +7721,7 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) |