diff options
Diffstat (limited to 'src/cairo-pdf-surface.c')
-rw-r--r-- | src/cairo-pdf-surface.c | 1247 |
1 files changed, 814 insertions, 433 deletions
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 49e5e84..00cc4fb 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -44,6 +44,7 @@ #include "cairo-pdf.h" #include "cairo-pdf-surface-private.h" #include "cairo-pdf-operators-private.h" +#include "cairo-pdf-shading-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-error-private.h" @@ -357,6 +358,8 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, goto BAIL1; } + _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE); + surface->next_available_resource.id = 1; surface->pages_resource = _cairo_pdf_surface_new_object (surface); if (surface->pages_resource.id == 0) { @@ -1037,13 +1040,24 @@ _cairo_pdf_source_surface_equal (const void *key_a, const void *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) && (a->interpolate == b->interpolate); + if (a->interpolate != b->interpolate) + return FALSE; + + if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length) + return (memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0); + + return (a->id == b->id); } static void _cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key) { - key->base.hash = key->id; + if (key->unique_id && key->unique_id_length > 0) { + key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE, + key->unique_id, key->unique_id_length); + } else { + key->base.hash = key->id; + } } static cairo_int_status_t @@ -1135,6 +1149,7 @@ static cairo_status_t _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, cairo_surface_t *source, cairo_filter_t filter, + cairo_bool_t mask, cairo_pdf_resource_t *surface_res, int *width, int *height) @@ -1144,6 +1159,8 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, cairo_pdf_source_surface_entry_t *surface_entry; cairo_status_t status; cairo_bool_t interpolate; + const unsigned char *unique_id; + unsigned long unique_id_length; switch (filter) { default: @@ -1161,6 +1178,9 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, surface_key.id = source->unique_id; surface_key.interpolate = interpolate; + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_UNIQUE_ID, + (const unsigned char **) &surface_key.unique_id, + &surface_key.unique_id_length); _cairo_pdf_source_surface_init_key (&surface_key); surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base); if (surface_entry) { @@ -1177,6 +1197,23 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, surface_entry->id = surface_key.id; surface_entry->interpolate = interpolate; + surface_entry->mask = mask; + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_UNIQUE_ID, + &unique_id, &unique_id_length); + if (unique_id && unique_id_length > 0) { + surface_entry->unique_id = malloc (unique_id_length); + if (surface_entry->unique_id == NULL) { + cairo_surface_destroy (source); + free (surface_entry); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + surface_entry->unique_id_length = unique_id_length; + memcpy (surface_entry->unique_id, unique_id, unique_id_length); + } else { + surface_entry->unique_id = NULL; + surface_entry->unique_id_length = 0; + } _cairo_pdf_source_surface_init_key (surface_entry); src_surface.hash_entry = surface_entry; @@ -1251,10 +1288,13 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, /* gradient patterns require an smask object to implement transparency */ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || - pattern->type == CAIRO_PATTERN_TYPE_RADIAL) + pattern->type == CAIRO_PATTERN_TYPE_RADIAL || + pattern->type == CAIRO_PATTERN_TYPE_MESH) { - cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; - if (! _gradient_stops_are_opaque (gradient)) { + double min_alpha; + + _cairo_pattern_alpha_range (pattern, &min_alpha, NULL); + if (! CAIRO_ALPHA_IS_OPAQUE (min_alpha)) { pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface); if (pdf_pattern.gstate_res.id == 0) { cairo_pattern_destroy (pdf_pattern.pattern); @@ -1629,6 +1669,9 @@ _cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) cairo_hash_table_t *patterns = closure; _cairo_hash_table_remove (patterns, &surface_entry->base); + if (surface_entry->unique_id) + free (surface_entry->unique_id); + free (surface_entry); } @@ -1783,6 +1826,46 @@ _cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface) return TRUE; } +static cairo_status_t +_cairo_pdf_surface_emit_imagemask (cairo_pdf_surface_t *surface, + cairo_image_surface_t *image, + cairo_pdf_resource_t *image_res) +{ + cairo_status_t status; + uint8_t *byte, output_byte; + int row, col, num_cols; + + + /* This is the only image format supported for stencil masking */ + assert (image->format == CAIRO_FORMAT_A1); + + status = _cairo_pdf_surface_open_stream (surface, + image_res, + TRUE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /ImageMask true\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 1\n", + image->width, image->height); + if (unlikely (status)) + return status; + + num_cols = (image->width + 7) / 8; + for (row = 0; row < image->height; row++) { + byte = image->data + row * image->stride; + for (col = 0; col < num_cols; col++) { + output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); + output_byte = ~output_byte; + _cairo_output_stream_write (surface->output, &output_byte, 1); + byte++; + } + } + + return _cairo_pdf_surface_close_stream (surface); +} + /* Emit alpha channel from the image into the given data, providing * an id that can be used to reference the resulting SMask object. * @@ -1891,7 +1974,8 @@ static cairo_status_t _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, cairo_image_surface_t *image, cairo_pdf_resource_t *image_res, - cairo_filter_t filter) + cairo_filter_t filter, + cairo_bool_t mask) { cairo_status_t status = CAIRO_STATUS_SUCCESS; char *rgb; @@ -1912,6 +1996,9 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, image->format == CAIRO_FORMAT_A8 || image->format == CAIRO_FORMAT_A1); + if (mask) + return _cairo_pdf_surface_emit_imagemask (surface, image, image_res); + rgb_size = image->height * image->width * 3; rgb = _cairo_malloc_abc (image->width, image->height, 3); if (unlikely (rgb == NULL)) { @@ -2108,7 +2195,8 @@ static cairo_status_t _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, cairo_surface_t *source, cairo_pdf_resource_t resource, - cairo_bool_t interpolate) + cairo_bool_t interpolate, + cairo_bool_t mask) { cairo_image_surface_t *image; void *image_extra; @@ -2127,7 +2215,7 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, return status; status = _cairo_pdf_surface_emit_image (surface, image, - &resource, interpolate); + &resource, interpolate, mask); if (unlikely (status)) goto BAIL; @@ -2218,7 +2306,7 @@ _cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t *surface, } status = _cairo_pdf_surface_emit_image (surface, (cairo_image_surface_t *)pad_image, - resource, interpolate); + resource, interpolate, FALSE); if (unlikely (status)) goto BAIL; @@ -2377,7 +2465,8 @@ _cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, return _cairo_pdf_surface_emit_image_surface (surface, src_surface->surface, src_surface->hash_entry->surface_res, - src_surface->hash_entry->interpolate); + src_surface->hash_entry->interpolate, + src_surface->hash_entry->mask); } } @@ -2414,6 +2503,7 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_add_source_surface (surface, pattern->surface, pdf_pattern->pattern->filter, + FALSE, &pattern_resource, &pattern_width, &pattern_height); @@ -2827,7 +2917,50 @@ _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, stops[n_stops-1].offset = 1.0; } - if (n_stops <= 2) { + if (stops[0].offset == stops[n_stops - 1].offset) { + /* + * The first and the last stops have the same offset, but we + * don't want a function with an empty domain, because that + * would provoke underdefined behaviour from rasterisers. + * This can only happen with EXTEND_PAD, because EXTEND_NONE + * is optimised into a clear pattern in cairo-gstate, and + * REFLECT/REPEAT are always transformed to have the first + * stop at t=0 and the last stop at t=1. Thus we want a step + * function going from the first color to the last one. + * + * This can be accomplished by stitching three functions: + * - a constant first color function, + * - a step from the first color to the last color (with empty domain) + * - a constant last color function + */ + cairo_pdf_color_stop_t pad_stops[4]; + + assert (pattern->base.extend == CAIRO_EXTEND_PAD); + + pad_stops[0] = pad_stops[1] = stops[0]; + pad_stops[2] = pad_stops[3] = stops[n_stops - 1]; + + pad_stops[0].offset = 0; + pad_stops[3].offset = 1; + + status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, + 4, + pad_stops, + FALSE, + color_function); + if (unlikely (status)) + goto BAIL; + + if (emit_alpha) { + status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, + 4, + pad_stops, + TRUE, + alpha_function); + if (unlikely (status)) + goto BAIL; + } + } else if (n_stops == 2) { /* no need for stitched function */ status = cairo_pdf_surface_emit_rgb_linear_function (surface, &stops[0], @@ -3013,101 +3146,159 @@ cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } +static void +_cairo_pdf_surface_output_gradient (cairo_pdf_surface_t *surface, + const cairo_pdf_pattern_t *pdf_pattern, + cairo_pdf_resource_t pattern_resource, + const cairo_matrix_t *pat_to_pdf, + const cairo_circle_double_t*start, + const cairo_circle_double_t*end, + const double *domain, + const char *colorspace, + cairo_pdf_resource_t color_function) +{ + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Pattern\n" + " /PatternType 2\n" + " /Matrix [ %f %f %f %f %f %f ]\n" + " /Shading\n", + pattern_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); + + if (pdf_pattern->pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { + _cairo_output_stream_printf (surface->output, + " << /ShadingType 2\n" + " /ColorSpace %s\n" + " /Coords [ %f %f %f %f ]\n", + colorspace, + start->center.x, start->center.y, + end->center.x, end->center.y); + } else { + _cairo_output_stream_printf (surface->output, + " << /ShadingType 3\n" + " /ColorSpace %s\n" + " /Coords [ %f %f %f %f %f %f ]\n", + colorspace, + start->center.x, start->center.y, + MAX (start->radius, 0), + end->center.x, end->center.y, + MAX (end->radius, 0)); + } + + _cairo_output_stream_printf (surface->output, + " /Domain [ %f %f ]\n", + domain[0], domain[1]); + + if (pdf_pattern->pattern->extend != CAIRO_EXTEND_NONE) { + _cairo_output_stream_printf (surface->output, + " /Extend [ true true ]\n"); + } else { + _cairo_output_stream_printf (surface->output, + " /Extend [ false false ]\n"); + } + + _cairo_output_stream_printf (surface->output, + " /Function %d 0 R\n" + " >>\n" + ">>\n" + "endobj\n", + color_function.id); +} + static cairo_status_t -_cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern) +_cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern) { - cairo_linear_pattern_t *pattern = (cairo_linear_pattern_t *) pdf_pattern->pattern; + cairo_gradient_pattern_t *pattern = (cairo_gradient_pattern_t *) pdf_pattern->pattern; cairo_pdf_resource_t color_function, alpha_function; - double x1, y1, x2, y2; - double _x1, _y1, _x2, _y2; cairo_matrix_t pat_to_pdf; - cairo_extend_t extend; + cairo_circle_double_t start, end; + double domain[2]; cairo_status_t status; - cairo_gradient_pattern_t *gradient = &pattern->base; - double first_stop, last_stop; - int repeat_begin = 0, repeat_end = 1; - assert (pattern->base.n_stops != 0); + assert (pattern->n_stops != 0); - extend = cairo_pattern_get_extend (pdf_pattern->pattern); + status = _cairo_pdf_surface_emit_pattern_stops (surface, + pattern, + &color_function, + &alpha_function); + if (unlikely (status)) + return status; - pat_to_pdf = pattern->base.base.matrix; + 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); - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); - first_stop = gradient->stops[0].offset; - last_stop = gradient->stops[gradient->n_stops - 1].offset; - - if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - double dx, dy; - int x_rep = 0, y_rep = 0; - - x1 = _cairo_fixed_to_double (pattern->p1.x); - y1 = _cairo_fixed_to_double (pattern->p1.y); - cairo_matrix_transform_point (&pat_to_pdf, &x1, &y1); - - x2 = _cairo_fixed_to_double (pattern->p2.x); - y2 = _cairo_fixed_to_double (pattern->p2.y); - cairo_matrix_transform_point (&pat_to_pdf, &x2, &y2); - - dx = fabs (x2 - x1); - dy = fabs (y2 - y1); - if (dx > 1e-6) - x_rep = ceil (surface->width/dx); - if (dy > 1e-6) - y_rep = ceil (surface->height/dy); - - repeat_end = MAX (x_rep, y_rep); - repeat_begin = -repeat_end; - first_stop = repeat_begin; - last_stop = repeat_end; - } - - /* PDF requires the first and last stop to be the same as the line - * coordinates. For repeating patterns this moves the line - * coordinates out to the begin/end of the repeating function. For - * non repeating patterns this may move the line coordinates in if - * there are not stops at offset 0 and 1. */ - x1 = _cairo_fixed_to_double (pattern->p1.x); - y1 = _cairo_fixed_to_double (pattern->p1.y); - x2 = _cairo_fixed_to_double (pattern->p2.x); - y2 = _cairo_fixed_to_double (pattern->p2.y); - - _x1 = x1 + (x2 - x1)*first_stop; - _y1 = y1 + (y2 - y1)*first_stop; - _x2 = x1 + (x2 - x1)*last_stop; - _y2 = y1 + (y2 - y1)*last_stop; - - x1 = _x1; - x2 = _x2; - y1 = _y1; - y2 = _y2; - - /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a - * Type 2 function is used by itself without a stitching - * function. Type 2 functions always have the domain [0 1] */ - if ((pattern->base.base.extend == CAIRO_EXTEND_NONE || - pattern->base.base.extend == CAIRO_EXTEND_PAD) && - gradient->n_stops == 2) { - first_stop = 0.0; - last_stop = 1.0; + + if (pattern->base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.extend == CAIRO_EXTEND_REFLECT) + { + double bounds_x1, bounds_x2, bounds_y1, bounds_y2; + double x_scale, y_scale, tolerance; + + /* TODO: use tighter extents */ + bounds_x1 = 0; + bounds_y1 = 0; + bounds_x2 = surface->width; + bounds_y2 = surface->height; + _cairo_matrix_transform_bounding_box (&pattern->base.matrix, + &bounds_x1, &bounds_y1, + &bounds_x2, &bounds_y2, + NULL); + + x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution; + y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution; + + tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix)); + tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1); + tolerance *= MIN (x_scale, y_scale); + + _cairo_gradient_pattern_box_to_parameter (pattern, + bounds_x1, bounds_y1, + bounds_x2, bounds_y2, + tolerance, domain); + } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) { + /* + * If the first and the last stop offset are the same, then + * the color function is a step function. + * _cairo_ps_surface_emit_pattern_stops emits it as a stitched + * function no matter how many stops the pattern has. The + * domain of the stitched function will be [0 1] in this case. + * + * This is done to avoid emitting degenerate gradients for + * EXTEND_PAD patterns having a step color function. + */ + domain[0] = 0.0; + domain[1] = 1.0; + + assert (pattern->base.extend == CAIRO_EXTEND_PAD); + } else { + domain[0] = pattern->stops[0].offset; + domain[1] = pattern->stops[pattern->n_stops - 1].offset; } - status = _cairo_pdf_surface_emit_pattern_stops (surface, - &pattern->base, - &color_function, - &alpha_function); - if (unlikely (status)) - return status; + /* PDF requires the first and last stop to be the same as the + * extreme coordinates. For repeating patterns this moves the + * extreme coordinates out to the begin/end of the repeating + * function. For non repeating patterns this may move the extreme + * coordinates in if there are not stops at offset 0 and 1. */ + _cairo_gradient_pattern_interpolate (pattern, domain[0], &start); + _cairo_gradient_pattern_interpolate (pattern, domain[1], &end); + + if (pattern->base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.extend == CAIRO_EXTEND_REFLECT) + { + int repeat_begin, repeat_end; + + repeat_begin = floor (domain[0]); + repeat_end = ceil (domain[1]); - if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { status = _cairo_pdf_surface_emit_repeating_function (surface, - &pattern->base, + pattern, &color_function, repeat_begin, repeat_end); @@ -3116,47 +3307,26 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, if (alpha_function.id != 0) { status = _cairo_pdf_surface_emit_repeating_function (surface, - &pattern->base, + pattern, &alpha_function, repeat_begin, repeat_end); if (unlikely (status)) return status; } + } else if (pattern->n_stops <= 2) { + /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a + * Type 2 function is used by itself without a stitching + * function. Type 2 functions always have the domain [0 1] */ + domain[0] = 0.0; + domain[1] = 1.0; } _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 2\n" - " /ColorSpace /DeviceRGB\n" - " /Coords [ %f %f %f %f ]\n" - " /Domain [ %f %f ]\n" - " /Function %d 0 R\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, - x1, y1, x2, y2, - first_stop, last_stop, - color_function.id); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } - - _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); + _cairo_pdf_surface_output_gradient (surface, pdf_pattern, + pdf_pattern->pattern_res, + &pat_to_pdf, &start, &end, domain, + "/DeviceRGB", color_function); if (alpha_function.id != 0) { cairo_pdf_resource_t mask_resource; @@ -3168,37 +3338,11 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, if (mask_resource.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 2\n" - " /ColorSpace /DeviceGray\n" - " /Coords [ %f %f %f %f ]\n" - " /Domain [ %f %f ]\n" - " /Function %d 0 R\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, - x1, y1, x2, y2, - first_stop, last_stop, - alpha_function.id); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } + _cairo_pdf_surface_output_gradient (surface, pdf_pattern, + mask_resource, + &pat_to_pdf, &start, &end, domain, + "/DeviceGray", alpha_function); - _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); status = _cairo_pdf_surface_add_pattern (surface, mask_resource); if (unlikely (status)) return status; @@ -3214,111 +3358,138 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, } static cairo_status_t -_cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern) +_cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern) { - cairo_pdf_resource_t color_function, alpha_function; - double x1, y1, x2, y2, r1, r2; cairo_matrix_t pat_to_pdf; - cairo_extend_t extend; cairo_status_t status; - cairo_radial_pattern_t *pattern = (cairo_radial_pattern_t *) pdf_pattern->pattern; + cairo_pattern_t *pattern = pdf_pattern->pattern; + cairo_pdf_shading_t shading; + int i; + cairo_pdf_resource_t res; - assert (pattern->base.n_stops != 0); + 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); - extend = cairo_pattern_get_extend (pdf_pattern->pattern); + cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); - status = _cairo_pdf_surface_emit_pattern_stops (surface, - &pattern->base, - &color_function, - &alpha_function); + status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern); if (unlikely (status)) return status; - pat_to_pdf = pattern->base.base.matrix; - status = cairo_matrix_invert (&pat_to_pdf); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); + res = _cairo_pdf_surface_new_object (surface); + if (unlikely (res.id == 0)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); - x1 = _cairo_fixed_to_double (pattern->c1.x); - y1 = _cairo_fixed_to_double (pattern->c1.y); - r1 = _cairo_fixed_to_double (pattern->r1); - x2 = _cairo_fixed_to_double (pattern->c2.x); - y2 = _cairo_fixed_to_double (pattern->c2.y); - r2 = _cairo_fixed_to_double (pattern->r2); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /ShadingType %d\n" + " /ColorSpace /DeviceRGB\n" + " /BitsPerCoordinate %d\n" + " /BitsPerComponent %d\n" + " /BitsPerFlag %d\n" + " /Decode [", + res.id, + shading.shading_type, + shading.bits_per_coordinate, + shading.bits_per_component, + shading.bits_per_flag); - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); + for (i = 0; i < shading.decode_array_length; i++) + _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]); + + _cairo_output_stream_printf (surface->output, + "]\n" + " /Length %ld\n" + ">>\n" + "stream\n", + shading.data_length); + + _cairo_output_stream_write (surface->output, shading.data, shading.data_length); _cairo_output_stream_printf (surface->output, + "\nendstream\n" + "endobj\n"); + + _cairo_pdf_shading_fini (&shading); + + _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); + _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Pattern\n" " /PatternType 2\n" " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 3\n" - " /ColorSpace /DeviceRGB\n" - " /Coords [ %f %f %f %f %f %f ]\n" - " /Function %d 0 R\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, - x1, y1, r1, x2, y2, r2, - color_function.id); + res.id); - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } + if (pdf_pattern->gstate_res.id != 0) { + cairo_pdf_resource_t mask_resource; - _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); + /* Create pattern for SMask. */ + res = _cairo_pdf_surface_new_object (surface); + if (unlikely (res.id == 0)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - if (alpha_function.id != 0) { - cairo_pdf_resource_t mask_resource; + status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern); + if (unlikely (status)) + return status; - assert (pdf_pattern->gstate_res.id != 0); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /ShadingType %d\n" + " /ColorSpace /DeviceGray\n" + " /BitsPerCoordinate %d\n" + " /BitsPerComponent %d\n" + " /BitsPerFlag %d\n" + " /Decode [", + res.id, + shading.shading_type, + shading.bits_per_coordinate, + shading.bits_per_component, + shading.bits_per_flag); + + for (i = 0; i < shading.decode_array_length; i++) + _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]); + + _cairo_output_stream_printf (surface->output, + "]\n" + " /Length %ld\n" + ">>\n" + "stream\n", + shading.data_length); + + _cairo_output_stream_write (surface->output, shading.data, shading.data_length); + + _cairo_output_stream_printf (surface->output, + "\nendstream\n" + "endobj\n"); + _cairo_pdf_shading_fini (&shading); - /* Create pattern for SMask. */ mask_resource = _cairo_pdf_surface_new_object (surface); - if (mask_resource.id == 0) + if (unlikely (mask_resource.id == 0)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 3\n" - " /ColorSpace /DeviceGray\n" - " /Coords [ %f %f %f %f %f %f ]\n" - " /Function %d 0 R\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, - x1, y1, r1, x2, y2, r2, - alpha_function.id); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } - - _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Pattern\n" + " /PatternType 2\n" + " /Matrix [ %f %f %f %f %f %f ]\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, pdf_pattern->gstate_res, @@ -3353,11 +3524,12 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern break; case CAIRO_PATTERN_TYPE_LINEAR: - status = _cairo_pdf_surface_emit_linear_pattern (surface, pdf_pattern); + case CAIRO_PATTERN_TYPE_RADIAL: + status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern); break; - case CAIRO_PATTERN_TYPE_RADIAL: - status = _cairo_pdf_surface_emit_radial_pattern (surface, pdf_pattern); + case CAIRO_PATTERN_TYPE_MESH: + status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern); break; default: @@ -3375,7 +3547,8 @@ _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_surface_pattern_t *source, + cairo_bool_t mask) { cairo_pdf_resource_t surface_res; int width, height; @@ -3386,6 +3559,7 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_add_source_surface (surface, source->surface, source->base.filter, + mask, &surface_res, &width, &height); @@ -3420,10 +3594,16 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; - _cairo_output_stream_printf (surface->output, - "/a%d gs /x%d Do\n", - alpha, - surface_res.id); + if (mask) { + _cairo_output_stream_printf (surface->output, + "/x%d Do\n", + surface_res.id); + } else { + _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); } @@ -3616,6 +3796,7 @@ _cairo_pdf_surface_get_font_options (void *abstract_surface, cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); } static cairo_pdf_resource_t @@ -3790,7 +3971,6 @@ _create_font_subset_tag (cairo_scaled_font_subset_t *font_subset, static cairo_int_status_t _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset, - cairo_bool_t is_composite, cairo_pdf_resource_t *stream) { unsigned int i, num_bfchar; @@ -3818,7 +3998,7 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, "/CMapType 2 def\n" "1 begincodespacerange\n"); - if (is_composite) { + if (font_subset->is_composite && !font_subset->is_latin) { _cairo_output_stream_printf (surface->output, "<0000> <ffff>\n"); } else { @@ -3870,7 +4050,7 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, "%d beginbfchar\n", num_bfchar - i > 100 ? 100 : num_bfchar - i); } - if (is_composite) + if (font_subset->is_composite && !font_subset->is_latin) _cairo_output_stream_printf (surface->output, "<%04x> ", i + 1); else _cairo_output_stream_printf (surface->output, "<%02x> ", i + 1); @@ -3908,7 +4088,7 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, cairo_pdf_resource_t stream, descriptor, cidfont_dict; cairo_pdf_resource_t subset_resource, to_unicode_stream; cairo_pdf_font_t font; - unsigned int i; + unsigned int i, last_glyph; cairo_status_t status; char tag[10]; @@ -3923,6 +4103,8 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_open_stream (surface, NULL, TRUE, + font_subset->is_latin ? + " /Subtype /Type1C\n" : " /Subtype /CIDFontType0C\n"); if (unlikely (status)) return status; @@ -3935,7 +4117,7 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, return status; status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, TRUE, + font_subset, &to_unicode_stream); if (_cairo_status_is_error (status)) return status; @@ -3979,58 +4161,106 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, (long)(subset->y_max*PDF_UNITS_PER_EM), stream.id); - cidfont_dict = _cairo_pdf_surface_new_object (surface); - if (cidfont_dict.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (font_subset->is_latin) { + /* find last glyph used */ + for (i = 255; i >= 32; i--) + if (font_subset->latin_to_subset_glyph_index[i] > 0) + break; - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /CIDFontType0\n" - " /BaseFont /%s+%s\n" - " /CIDSystemInfo\n" - " << /Registry (Adobe)\n" - " /Ordering (Identity)\n" - " /Supplement 0\n" - " >>\n" - " /FontDescriptor %d 0 R\n" - " /W [0 [", - cidfont_dict.id, - tag, - subset->ps_name, - descriptor.id); + last_glyph = i; + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\r\n" + "<< /Type /Font\n" + " /Subtype /Type1\n" + " /BaseFont /%s+%s\n" + " /FirstChar 32\n" + " /LastChar %d\n" + " /FontDescriptor %d 0 R\n" + " /Encoding /WinAnsiEncoding\n" + " /Widths [", + subset_resource.id, + tag, + subset->ps_name, + last_glyph, + descriptor.id); + + for (i = 32; i < last_glyph + 1; i++) { + int glyph = font_subset->latin_to_subset_glyph_index[i]; + if (glyph > 0) { + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset->widths[glyph]*PDF_UNITS_PER_EM)); + } else { + _cairo_output_stream_printf (surface->output, " 0"); + } + } - for (i = 0; i < font_subset->num_glyphs; i++) _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset->widths[i]*PDF_UNITS_PER_EM)); + " ]\n"); - _cairo_output_stream_printf (surface->output, - " ]]\n" - ">>\n" - "endobj\n"); + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type0\n" - " /BaseFont /%s+%s\n" - " /Encoding /Identity-H\n" - " /DescendantFonts [ %d 0 R]\n", - subset_resource.id, - tag, - subset->ps_name, - cidfont_dict.id); + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + } else { + cidfont_dict = _cairo_pdf_surface_new_object (surface); + if (cidfont_dict.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /CIDFontType0\n" + " /BaseFont /%s+%s\n" + " /CIDSystemInfo\n" + " << /Registry (Adobe)\n" + " /Ordering (Identity)\n" + " /Supplement 0\n" + " >>\n" + " /FontDescriptor %d 0 R\n" + " /W [0 [", + cidfont_dict.id, + tag, + subset->ps_name, + descriptor.id); + + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset->widths[i]*PDF_UNITS_PER_EM)); - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); + _cairo_output_stream_printf (surface->output, + " ]]\n" + ">>\n" + "endobj\n"); + + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type0\n" + " /BaseFont /%s+%s\n" + " /Encoding /Identity-H\n" + " /DescendantFonts [ %d 0 R]\n", + subset_resource.id, + tag, + subset->ps_name, + cidfont_dict.id); + + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + } font.font_id = font_subset->font_id; font.subset_id = font_subset->subset_id; @@ -4069,6 +4299,11 @@ _cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface, cairo_cff_subset_t subset; char name[64]; + /* CFF fallback subsetting does not work with 8-bit glyphs unless + * they are a latin subset */ + if (!font_subset->is_composite && !font_subset->is_latin) + return CAIRO_INT_STATUS_UNSUPPORTED; + snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_cff_fallback_init (&subset, name, font_subset); @@ -4091,7 +4326,7 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, cairo_pdf_font_t font; cairo_status_t status; unsigned long length; - unsigned int i; + unsigned int i, last_glyph; char tag[10]; _create_font_subset_tag (font_subset, subset->base_font, tag); @@ -4122,11 +4357,20 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, return status; status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, FALSE, + font_subset, &to_unicode_stream); if (_cairo_status_is_error (status)) return status; + if (font_subset->is_latin) { + /* find last glyph used */ + for (i = 255; i >= 32; i--) + if (font_subset->latin_to_subset_glyph_index[i] > 0) + break; + + last_glyph = i; + } + descriptor = _cairo_pdf_surface_new_object (surface); if (descriptor.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -4135,7 +4379,7 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, "%d 0 obj\n" "<< /Type /FontDescriptor\n" " /FontName /%s+%s\n" - " /Flags 4\n" + " /Flags %d\n" " /FontBBox [ %ld %ld %ld %ld ]\n" " /ItalicAngle 0\n" " /Ascent %ld\n" @@ -4149,6 +4393,7 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, descriptor.id, tag, subset->base_font, + font_subset->is_latin ? 32 : 4, (long)(subset->x_min*PDF_UNITS_PER_EM), (long)(subset->y_min*PDF_UNITS_PER_EM), (long)(subset->x_max*PDF_UNITS_PER_EM), @@ -4164,20 +4409,34 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, "<< /Type /Font\n" " /Subtype /Type1\n" " /BaseFont /%s+%s\n" - " /FirstChar 0\n" + " /FirstChar %d\n" " /LastChar %d\n" " /FontDescriptor %d 0 R\n" " /Widths [", subset_resource.id, tag, subset->base_font, - font_subset->num_glyphs - 1, + font_subset->is_latin ? 32 : 0, + font_subset->is_latin ? last_glyph : font_subset->num_glyphs - 1, descriptor.id); - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset->widths[i]*PDF_UNITS_PER_EM)); + if (font_subset->is_latin) { + for (i = 32; i < last_glyph + 1; i++) { + int glyph = font_subset->latin_to_subset_glyph_index[i]; + if (glyph > 0) { + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset->widths[glyph]*PDF_UNITS_PER_EM)); + } else { + _cairo_output_stream_printf (surface->output, " 0"); + } + } + } else { + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset->widths[i]*PDF_UNITS_PER_EM)); + } _cairo_output_stream_printf (surface->output, " ]\n"); @@ -4206,6 +4465,10 @@ _cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface, cairo_type1_subset_t subset; char name[64]; + /* 16-bit glyphs not compatible with Type 1 fonts */ + if (font_subset->is_composite && !font_subset->is_latin) + return CAIRO_INT_STATUS_UNSUPPORTED; + snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE); @@ -4227,6 +4490,10 @@ _cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface, cairo_type1_subset_t subset; char name[64]; + /* 16-bit glyphs not compatible with Type 1 fonts */ + if (font_subset->is_composite && !font_subset->is_latin) + return CAIRO_INT_STATUS_UNSUPPORTED; + snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_fallback_init_binary (&subset, name, font_subset); @@ -4248,7 +4515,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, cairo_status_t status; cairo_pdf_font_t font; cairo_truetype_subset_t subset; - unsigned int i; + unsigned int i, last_glyph; char tag[10]; subset_resource = _cairo_pdf_surface_get_font_resource (surface, @@ -4257,7 +4524,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, if (subset_resource.id == 0) return CAIRO_STATUS_SUCCESS; - status = _cairo_truetype_subset_init (&subset, font_subset); + status = _cairo_truetype_subset_init_pdf (&subset, font_subset); if (unlikely (status)) return status; @@ -4283,7 +4550,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, } status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, TRUE, + font_subset, &to_unicode_stream); if (_cairo_status_is_error (status)) { _cairo_truetype_subset_fini (&subset); @@ -4311,7 +4578,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, } _cairo_output_stream_printf (surface->output, - " /Flags 4\n" + " /Flags %d\n" " /FontBBox [ %ld %ld %ld %ld ]\n" " /ItalicAngle 0\n" " /Ascent %ld\n" @@ -4322,6 +4589,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, " /FontFile2 %u 0 R\n" ">>\n" "endobj\n", + font_subset->is_latin ? 32 : 4, (long)(subset.x_min*PDF_UNITS_PER_EM), (long)(subset.y_min*PDF_UNITS_PER_EM), (long)(subset.x_max*PDF_UNITS_PER_EM), @@ -4331,60 +4599,108 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, (long)(subset.y_max*PDF_UNITS_PER_EM), stream.id); - cidfont_dict = _cairo_pdf_surface_new_object (surface); - if (cidfont_dict.id == 0) { - _cairo_truetype_subset_fini (&subset); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } + if (font_subset->is_latin) { + /* find last glyph used */ + for (i = 255; i >= 32; i--) + if (font_subset->latin_to_subset_glyph_index[i] > 0) + break; - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /CIDFontType2\n" - " /BaseFont /%s+%s\n" - " /CIDSystemInfo\n" - " << /Registry (Adobe)\n" - " /Ordering (Identity)\n" - " /Supplement 0\n" - " >>\n" - " /FontDescriptor %d 0 R\n" - " /W [0 [", - cidfont_dict.id, - tag, - subset.ps_name, - descriptor.id); + last_glyph = i; + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\r\n" + "<< /Type /Font\n" + " /Subtype /TrueType\n" + " /BaseFont /%s+%s\n" + " /FirstChar 32\n" + " /LastChar %d\n" + " /FontDescriptor %d 0 R\n" + " /Encoding /WinAnsiEncoding\n" + " /Widths [", + subset_resource.id, + tag, + subset.ps_name, + last_glyph, + descriptor.id); + + for (i = 32; i < last_glyph + 1; i++) { + int glyph = font_subset->latin_to_subset_glyph_index[i]; + if (glyph > 0) { + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset.widths[glyph]*PDF_UNITS_PER_EM)); + } else { + _cairo_output_stream_printf (surface->output, " 0"); + } + } - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset.widths[i]*PDF_UNITS_PER_EM)); + _cairo_output_stream_printf (surface->output, + " ]\n"); - _cairo_output_stream_printf (surface->output, - " ]]\n" - ">>\n" - "endobj\n"); + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type0\n" - " /BaseFont /%s+%s\n" - " /Encoding /Identity-H\n" - " /DescendantFonts [ %d 0 R]\n", - subset_resource.id, - tag, - subset.ps_name, - cidfont_dict.id); + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + } else { + cidfont_dict = _cairo_pdf_surface_new_object (surface); + if (cidfont_dict.id == 0) { + _cairo_truetype_subset_fini (&subset); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /CIDFontType2\n" + " /BaseFont /%s+%s\n" + " /CIDSystemInfo\n" + " << /Registry (Adobe)\n" + " /Ordering (Identity)\n" + " /Supplement 0\n" + " >>\n" + " /FontDescriptor %d 0 R\n" + " /W [0 [", + cidfont_dict.id, + tag, + subset.ps_name, + descriptor.id); + + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset.widths[i]*PDF_UNITS_PER_EM)); - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); + _cairo_output_stream_printf (surface->output, + " ]]\n" + ">>\n" + "endobj\n"); + + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type0\n" + " /BaseFont /%s+%s\n" + " /Encoding /Identity-H\n" + " /DescendantFonts [ %d 0 R]\n", + subset_resource.id, + tag, + subset.ps_name, + cidfont_dict.id); + + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + } font.font_id = font_subset->font_id; font.subset_id = font_subset->subset_id; @@ -4607,7 +4923,7 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, free (glyphs); status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, FALSE, + font_subset, &to_unicode_stream); if (_cairo_status_is_error (status)) { free (widths); @@ -4668,30 +4984,27 @@ _cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_s cairo_pdf_surface_t *surface = closure; cairo_status_t status; - if (font_subset->is_composite) { - status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } else { #if CAIRO_HAS_FT_FONT - status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; #endif - status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - } + status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; ASSERT_NOT_REACHED; return CAIRO_STATUS_SUCCESS; @@ -5032,7 +5345,7 @@ _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; - int pattern_index, group_index, surface_index; + unsigned int pattern_index, group_index, surface_index; cairo_status_t status; /* Writing out PDF_MASK groups will cause additional smask groups @@ -5079,7 +5392,7 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t page, knockout, res; cairo_status_t status; - int i, len; + unsigned int i, len; _cairo_pdf_group_resources_clear (&surface->resources); if (surface->has_fallback_images) { @@ -5226,54 +5539,22 @@ _surface_pattern_supported (cairo_surface_pattern_t *pattern) } static cairo_bool_t -_gradient_pattern_supported (const cairo_pattern_t *pattern) -{ - cairo_extend_t extend; - - extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern); - - - /* Radial gradients are currently only supported with EXTEND_NONE - * and EXTEND_PAD and when one circle is inside the other. */ - if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { - double x1, y1, x2, y2, r1, r2, d; - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - - if (extend == CAIRO_EXTEND_REPEAT || - extend == CAIRO_EXTEND_REFLECT) { - return FALSE; - } - - x1 = _cairo_fixed_to_double (radial->c1.x); - y1 = _cairo_fixed_to_double (radial->c1.y); - r1 = _cairo_fixed_to_double (radial->r1); - x2 = _cairo_fixed_to_double (radial->c2.x); - y2 = _cairo_fixed_to_double (radial->c2.y); - r2 = _cairo_fixed_to_double (radial->r2); - - d = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); - if (d > fabs(r2 - r1)) { - return FALSE; - } - } - - return TRUE; -} - -static cairo_bool_t _pattern_supported (const cairo_pattern_t *pattern) { - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: return TRUE; - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || - pattern->type == CAIRO_PATTERN_TYPE_RADIAL) - return _gradient_pattern_supported (pattern); - - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) + case CAIRO_PATTERN_TYPE_SURFACE: return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern); - return FALSE; + default: + ASSERT_NOT_REACHED; + return FALSE; + } } static cairo_bool_t @@ -5408,6 +5689,68 @@ _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) return _cairo_pdf_surface_open_content_stream (surface, NULL, TRUE); } +/* 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, + const cairo_pattern_t *source, + const cairo_pattern_t *mask) +{ + cairo_status_t status; + cairo_surface_pattern_t *surface_pattern; + cairo_image_surface_t *image; + void *image_extra; + cairo_pdf_resource_t pattern_res = {0}; + + if (! (source->type == CAIRO_PATTERN_TYPE_SOLID && + mask->type == CAIRO_PATTERN_TYPE_SURFACE)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + surface_pattern = (cairo_surface_pattern_t *) mask; + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_surface_acquire_source_image (surface_pattern->surface, + &image, + &image_extra); + if (unlikely (status)) + return status; + + if (image->base.status) + return image->base.status; + + if (image->format != CAIRO_FORMAT_A1) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + status = _cairo_pdf_surface_select_pattern (surface, source, + pattern_res, FALSE); + 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, + (cairo_surface_pattern_t *) surface_pattern, + TRUE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + + _cairo_surface_release_source_image (surface_pattern->surface, image, image_extra); + status = _cairo_output_stream_get_status (surface->output); + +cleanup: + _cairo_surface_release_source_image (surface_pattern->surface, image, image_extra); + + return status; +} + + static cairo_int_status_t _cairo_pdf_surface_paint (void *abstract_surface, cairo_operator_t op, @@ -5457,7 +5800,8 @@ _cairo_pdf_surface_paint (void *abstract_surface, { _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_surface_paint_surface_pattern (surface, - (cairo_surface_pattern_t *) source); + (cairo_surface_pattern_t *) source, + FALSE); if (unlikely (status)) return status; @@ -5524,7 +5868,7 @@ _cairo_pdf_surface_paint (void *abstract_surface, } static cairo_int_status_t -_cairo_pdf_surface_mask (void *abstract_surface, +_cairo_pdf_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, @@ -5575,6 +5919,15 @@ _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; + + /* Check if we can use a stencil mask */ + status = _cairo_pdf_surface_emit_stencil_mask (surface, source, mask); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + group = _cairo_pdf_surface_create_smask_group (surface); if (unlikely (group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -5614,10 +5967,6 @@ _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, @@ -5829,7 +6178,8 @@ _cairo_pdf_surface_fill (void *abstract_surface, return status; status = _cairo_pdf_surface_paint_surface_pattern (surface, - (cairo_surface_pattern_t *) source); + (cairo_surface_pattern_t *) source, + FALSE); if (unlikely (status)) return status; @@ -5927,7 +6277,7 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, 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; + cairo_composite_rectangles_t extents; /* During analysis we return unsupported and let the _fill and * _stroke functions that are on the fallback path do the analysis @@ -5959,18 +6309,32 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, 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; + status = _cairo_composite_rectangles_init_for_fill (&extents, + surface->width, + surface->height, + fill_op, fill_source, path, + clip); + if (unlikely (status)) { + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + return status; + } + /* use the more accurate extents */ + if (extents.is_bounded) { + cairo_bool_t is_empty; + + _cairo_path_fixed_fill_extents (path, + fill_rule, + fill_tolerance, + &extents.mask); + + is_empty = ! _cairo_rectangle_intersect (&extents.bounded, &extents.mask); + } fill_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, - &extents, + &extents.bounded, &fill_pattern_res, &gstate_res); if (unlikely (status)) @@ -5978,19 +6342,36 @@ _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; + status = _cairo_composite_rectangles_init_for_stroke (&extents, + surface->width, + surface->height, + stroke_op, stroke_source, + path, stroke_style, stroke_ctm, + clip); + if (unlikely (status)) { + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + return status; + } + + /* use the more accurate extents */ + if (extents.is_bounded) { + cairo_bool_t is_empty; + + status = _cairo_path_fixed_stroke_extents (path, stroke_style, + stroke_ctm, stroke_ctm_inverse, + stroke_tolerance, + &extents.mask); + if (unlikely (status)) + return status; + + is_empty = ! _cairo_rectangle_intersect (&extents.bounded, &extents.mask); + } stroke_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, stroke_source, - &extents, + &extents.bounded, &stroke_pattern_res, &gstate_res); if (unlikely (status)) |