diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cairo-analysis-surface.c | 11 | ||||
-rw-r--r-- | src/cairo-cache.c | 7 | ||||
-rw-r--r-- | src/cairo-paginated-surface.c | 9 | ||||
-rw-r--r-- | src/cairo-pdf-surface-private.h | 10 | ||||
-rw-r--r-- | src/cairo-pdf-surface.c | 407 | ||||
-rw-r--r-- | src/cairo-ps-surface.c | 13 | ||||
-rw-r--r-- | src/cairo-recording-surface.c | 9 | ||||
-rw-r--r-- | src/cairo-scaled-font-subsets-private.h | 35 | ||||
-rw-r--r-- | src/cairo-scaled-font-subsets.c | 92 | ||||
-rw-r--r-- | src/cairo-surface-backend-private.h | 14 | ||||
-rw-r--r-- | src/cairo-surface.c | 32 | ||||
-rw-r--r-- | src/cairo-svg-surface.c | 8 | ||||
-rw-r--r-- | src/cairo-type3-glyph-surface.c | 14 | ||||
-rw-r--r-- | src/cairo-user-font.c | 4 | ||||
-rw-r--r-- | src/cairoint.h | 9 |
15 files changed, 531 insertions, 143 deletions
diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c index 86e10d5b0..0e7ba8a38 100644 --- a/src/cairo-analysis-surface.c +++ b/src/cairo-analysis-surface.c @@ -831,6 +831,14 @@ _cairo_analysis_surface_tag (void *abstract_surface, return backend_status; } +static cairo_bool_t +_cairo_analysis_surface_supports_color_glyph (void *abstract_surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + return TRUE; +} + static const cairo_surface_backend_t cairo_analysis_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, @@ -865,7 +873,8 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { _cairo_analysis_surface_has_show_text_glyphs, _cairo_analysis_surface_show_text_glyphs, NULL, /* get_supported_mime_types */ - _cairo_analysis_surface_tag + _cairo_analysis_surface_tag, + _cairo_analysis_surface_supports_color_glyph }; cairo_surface_t * diff --git a/src/cairo-cache.c b/src/cairo-cache.c index 96809b585..be1285a20 100644 --- a/src/cairo-cache.c +++ b/src/cairo-cache.c @@ -336,3 +336,10 @@ _cairo_hash_bytes (uintptr_t hash, hash = ((hash << 5) + hash) + *bytes++; return hash; } + +uintptr_t +_cairo_hash_uintptr (uintptr_t hash, + uintptr_t u) +{ + return _cairo_hash_bytes (hash, &u, sizeof(u)); +} diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c index 278c1a641..e079a9a28 100644 --- a/src/cairo-paginated-surface.c +++ b/src/cairo-paginated-surface.c @@ -756,6 +756,14 @@ _cairo_paginated_surface_tag (void *abstract_surface, begin, tag_name, attributes); } +static cairo_bool_t +_cairo_paginated_surface_supports_color_glyph (void *abstract_surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + return TRUE; +} + static cairo_surface_t * _cairo_paginated_surface_snapshot (void *abstract_other) { @@ -811,4 +819,5 @@ static const cairo_surface_backend_t cairo_paginated_surface_backend = { _cairo_paginated_surface_show_text_glyphs, _cairo_paginated_surface_get_supported_mime_types, _cairo_paginated_surface_tag, + _cairo_paginated_surface_supports_color_glyph, }; diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h index 0781d9c53..c97affe6b 100644 --- a/src/cairo-pdf-surface-private.h +++ b/src/cairo-pdf-surface-private.h @@ -258,6 +258,13 @@ typedef struct _cairo_pdf_interchange { } cairo_pdf_interchange_t; +typedef struct _cairo_pdf_color_glyph { + cairo_hash_entry_t base; + cairo_scaled_font_t *scaled_font; + unsigned long glyph_index; + cairo_bool_t supported; +} cairo_pdf_color_glyph_t; + /* pdf surface data */ typedef struct _cairo_pdf_surface cairo_pdf_surface_t; @@ -288,6 +295,7 @@ struct _cairo_pdf_surface { cairo_array_t knockout_group; cairo_array_t jbig2_global; cairo_array_t page_heights; + cairo_hash_table_t *color_glyphs; cairo_scaled_font_subsets_t *font_subsets; cairo_array_t fonts; @@ -335,11 +343,13 @@ struct _cairo_pdf_surface { cairo_pdf_operators_t pdf_operators; cairo_paginated_mode_t paginated_mode; + cairo_bool_t type3_replay; cairo_bool_t select_pattern_gstate_saved; cairo_bool_t force_fallbacks; cairo_operator_t current_operator; + cairo_bool_t reset_gs_required; cairo_bool_t current_pattern_is_solid_color; cairo_bool_t current_color_is_stroke; double current_color_red; diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 28e01ea5b..772828eb8 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -280,7 +280,8 @@ typedef struct _cairo_pdf_alpha_linear_function { } cairo_pdf_alpha_linear_function_t; static void -_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface); +_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface, + cairo_bool_t clear_doc_surfaces); static void _cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group); @@ -338,6 +339,9 @@ _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); static cairo_bool_t _cairo_pdf_source_surface_equal (const void *key_a, const void *key_b); +static cairo_bool_t +_cairo_pdf_color_glyph_equal (const void *key_a, const void *key_b); + static const cairo_surface_backend_t cairo_pdf_surface_backend; static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend; @@ -485,12 +489,18 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, goto BAIL0; } + surface->color_glyphs = _cairo_hash_table_create (_cairo_pdf_color_glyph_equal); + if (unlikely (surface->color_glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL1; + } + _cairo_pdf_group_resources_init (&surface->resources); surface->font_subsets = _cairo_scaled_font_subsets_create_composite (); if (! surface->font_subsets) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL1; + goto BAIL2; } _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE); @@ -499,7 +509,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->pages_resource = _cairo_pdf_surface_new_object (surface); if (surface->pages_resource.id == 0) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL2; + goto BAIL3; } surface->struct_tree_root.id = 0; @@ -515,11 +525,13 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_array_init (&surface->object_stream.objects, sizeof (cairo_xref_stream_object_t)); surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + surface->type3_replay = FALSE; surface->force_fallbacks = FALSE; surface->select_pattern_gstate_saved = FALSE; surface->current_pattern_is_solid_color = FALSE; surface->current_operator = CAIRO_OPERATOR_OVER; + surface->reset_gs_required = FALSE; surface->header_emitted = FALSE; _cairo_surface_clipper_init (&surface->clipper, @@ -537,7 +549,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, status = _cairo_pdf_interchange_init (surface); if (unlikely (status)) - goto BAIL2; + goto BAIL3; surface->page_parent_tree = -1; _cairo_array_init (&surface->page_annots, sizeof (cairo_pdf_resource_t)); @@ -568,8 +580,10 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, return surface->paginated_surface; } -BAIL2: +BAIL3: _cairo_scaled_font_subsets_destroy (surface->font_subsets); +BAIL2: + _cairo_hash_table_destroy (surface->color_glyphs); BAIL1: _cairo_hash_table_destroy (surface->all_surfaces); BAIL0: @@ -998,12 +1012,14 @@ cairo_pdf_surface_set_thumbnail_size (cairo_surface_t *surface, } static void -_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) +_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface, + cairo_bool_t clear_doc_surfaces) { int i, size; cairo_pdf_pattern_t *pattern; cairo_pdf_source_surface_t *src_surface; cairo_pdf_smask_group_t *group; + cairo_pdf_source_surface_t doc_surface; size = _cairo_array_num_elements (&surface->page_patterns); for (i = 0; i < size; i++) { @@ -1037,6 +1053,21 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) if (surface->thumbnail_image) cairo_surface_destroy (&surface->thumbnail_image->base); surface->thumbnail_image = NULL; + + if (clear_doc_surfaces) { + size = _cairo_array_num_elements (&surface->doc_surfaces); + for (i = 0; i < size; i++) { + _cairo_array_copy_element (&surface->doc_surfaces, i, &doc_surface); + if (doc_surface.type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + cairo_pattern_destroy (doc_surface.raster_pattern); + } else { + if (_cairo_surface_is_recording (doc_surface.surface) && doc_surface.region_id != 0) + _cairo_recording_surface_region_array_remove (doc_surface.surface, doc_surface.region_id); + cairo_surface_destroy (doc_surface.surface); + } + } + _cairo_array_truncate (&surface->doc_surfaces, 0); + } } static void @@ -1443,6 +1474,25 @@ _cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key) } } +static cairo_bool_t +_cairo_pdf_color_glyph_equal (const void *key_a, const void *key_b) +{ + const cairo_pdf_color_glyph_t *a = key_a; + const cairo_pdf_color_glyph_t *b = key_b; + + if (a->scaled_font != b->scaled_font) + return FALSE; + + return (a->glyph_index == b->glyph_index); +} + +static void +_cairo_pdf_color_glyph_init_key (cairo_pdf_color_glyph_t *key) +{ + key->base.hash = _cairo_hash_uintptr (_CAIRO_HASH_INIT_VALUE, (uintptr_t)key->scaled_font); + key->base.hash = _cairo_hash_uintptr (key->base.hash, key->glyph_index); +} + static cairo_int_status_t _cairo_pdf_surface_acquire_source_image_from_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, @@ -2131,7 +2181,7 @@ _cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, surface->group_stream.bbox = *bbox; /* Reset gstate */ - _cairo_output_stream_printf (surface->output, "/gs0 gs\n"); + surface->reset_gs_required = TRUE; surface->current_pattern_is_solid_color = FALSE; surface->current_operator = CAIRO_OPERATOR_OVER; _cairo_pdf_operators_reset (&surface->pdf_operators); @@ -2488,6 +2538,18 @@ _cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) free (surface_entry); } +static void +_cairo_pdf_color_glyph_pluck (void *entry, void *closure) +{ + cairo_pdf_color_glyph_t *glyph_entry = entry; + cairo_hash_table_t *patterns = closure; + + _cairo_hash_table_remove (patterns, &glyph_entry->base); + cairo_scaled_font_destroy (glyph_entry->scaled_font); + + free (glyph_entry); +} + static cairo_status_t _cairo_pdf_surface_finish (void *abstract_surface) { @@ -2496,7 +2558,6 @@ _cairo_pdf_surface_finish (void *abstract_surface) cairo_pdf_resource_t catalog; cairo_status_t status, status2; int size, i; - cairo_pdf_source_surface_t doc_surface; cairo_pdf_jbig2_global_t *global; char *label; cairo_pdf_resource_t xref_res; @@ -2505,7 +2566,7 @@ _cairo_pdf_surface_finish (void *abstract_surface) if (surface->base.status != CAIRO_STATUS_SUCCESS) goto CLEANUP; - _cairo_pdf_surface_clear (surface); + _cairo_pdf_surface_clear (surface, FALSE); status = _cairo_pdf_surface_open_object_stream (surface); if (unlikely (status)) @@ -2514,10 +2575,17 @@ _cairo_pdf_surface_finish (void *abstract_surface) /* Emit unbounded surfaces */ _cairo_pdf_surface_write_patterns_and_smask_groups (surface, TRUE); + _cairo_pdf_surface_clear (surface, TRUE); + status = surface->base.status; if (status == CAIRO_STATUS_SUCCESS) status = _cairo_pdf_surface_emit_font_subsets (surface); + /* Emit any new patterns or surfaces created by the Type 3 font subset. */ + _cairo_pdf_surface_write_patterns_and_smask_groups (surface, TRUE); + + _cairo_pdf_surface_clear (surface, TRUE); + status = _cairo_pdf_surface_write_pages (surface); if (unlikely (status)) return status; @@ -2608,17 +2676,6 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->page_surfaces); _cairo_array_fini (&surface->object_stream.objects); - size = _cairo_array_num_elements (&surface->doc_surfaces); - for (i = 0; i < size; i++) { - _cairo_array_copy_element (&surface->doc_surfaces, i, &doc_surface); - if (doc_surface.type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { - cairo_pattern_destroy (doc_surface.raster_pattern); - } else { - if (_cairo_surface_is_recording (doc_surface.surface) && doc_surface.region_id != 0) - _cairo_recording_surface_region_array_remove (doc_surface.surface, doc_surface.region_id); - cairo_surface_destroy (doc_surface.surface); - } - } _cairo_array_fini (&surface->doc_surfaces); _cairo_hash_table_foreach (surface->all_surfaces, _cairo_pdf_source_surface_entry_pluck, @@ -2630,6 +2687,11 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->page_annots); _cairo_array_fini (&surface->forward_links); + _cairo_hash_table_foreach (surface->color_glyphs, + _cairo_pdf_color_glyph_pluck, + surface->color_glyphs); + _cairo_hash_table_destroy (surface->color_glyphs); + if (surface->font_subsets) { _cairo_scaled_font_subsets_destroy (surface->font_subsets); surface->font_subsets = NULL; @@ -3727,7 +3789,7 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, goto err; /* Reset gstate */ - _cairo_output_stream_printf (surface->output, "/gs0 gs\n"); + surface->reset_gs_required = TRUE; if (source->content == CAIRO_CONTENT_COLOR) { status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); @@ -5080,6 +5142,7 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, alpha != 1.0, /* need_transp_group */ extents, smask_res, + &pdf_source, &x_offset, &y_offset, @@ -5267,6 +5330,11 @@ _cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, { cairo_int_status_t status; + if (surface->reset_gs_required) { + _cairo_output_stream_printf (surface->output, "/gs0 gs\n"); + surface->reset_gs_required = FALSE; + } + if (op == surface->current_operator) return CAIRO_STATUS_SUCCESS; @@ -5430,7 +5498,7 @@ _cairo_pdf_surface_show_page (void *abstract_surface) if (unlikely (status)) return status; - _cairo_pdf_surface_clear (surface); + _cairo_pdf_surface_clear (surface, FALSE); return CAIRO_STATUS_SUCCESS; } @@ -6480,6 +6548,208 @@ _cairo_pdf_emit_imagemask (cairo_image_surface_t *image, } static cairo_int_status_t +cairo_pdf_surface_emit_color_glyph (cairo_pdf_surface_t *surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index, + cairo_box_t *bbox, + double *width) +{ + cairo_rectangle_int_t extents; + cairo_scaled_glyph_t *scaled_glyph; + cairo_matrix_t mat; + cairo_int_status_t status; + double x_advance, y_advance; + cairo_matrix_t font_matrix_inverse; + cairo_surface_t *analysis; + cairo_rectangle_int_t old_surface_extents; + cairo_bool_t old_surface_bounded; + cairo_paginated_mode_t old_paginated_mode; + cairo_surface_t *glyph_surface = NULL; + unsigned int regions_id = 0; + cairo_surface_pattern_t surface_pattern; + + _cairo_scaled_font_freeze_cache (scaled_font); + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + if (status == CAIRO_INT_STATUS_SUCCESS) + glyph_surface = cairo_surface_reference (scaled_glyph->recording_surface); + + _cairo_scaled_font_thaw_cache (scaled_font); + if (unlikely (status)) + return status; + + analysis = _cairo_analysis_surface_create (&surface->base, TRUE); + if (unlikely (analysis->status)) { + status = _cairo_surface_set_error (&surface->base, analysis->status); + goto cleanup; + } + + extents.x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x); + extents.y = _cairo_fixed_to_double (scaled_glyph->bbox.p1.y); + extents.width = _cairo_fixed_to_double (scaled_glyph->bbox.p2.x) - extents.x; + extents.height = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y) - extents.y; + + old_surface_extents = surface->surface_extents; + old_surface_bounded = surface->surface_bounded; + old_paginated_mode = surface->paginated_mode; + surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + surface->type3_replay = TRUE; + surface->surface_extents = extents; + surface->surface_bounded = TRUE; + + status = _cairo_recording_surface_region_array_attach (glyph_surface, ®ions_id); + if (status) + goto cleanup; + + status = _cairo_recording_surface_replay_and_create_regions (glyph_surface, regions_id, + NULL, analysis, TRUE); + if (status) + goto cleanup; + + surface->surface_extents = old_surface_extents; + surface->surface_bounded = old_surface_bounded; + surface->paginated_mode = old_paginated_mode; + surface->type3_replay = FALSE; + + if (status == CAIRO_INT_STATUS_SUCCESS && + _cairo_analysis_surface_has_unsupported (analysis)) + { + status = CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_surface_destroy (analysis); + if (status) + goto cleanup; + + _cairo_pattern_init_for_surface (&surface_pattern, glyph_surface); + surface_pattern.region_array_id = regions_id; + + cairo_matrix_init_identity (&mat); + cairo_matrix_multiply (&mat, &mat, &scaled_font->scale_inverse); + + /* transform glyph extents to operation space */ + cairo_box_t box; + _cairo_box_from_rectangle (&box, &extents); + _cairo_matrix_transform_bounding_box_fixed (&mat, &box, NULL); + _cairo_box_round_to_rectangle (&box, &extents); + + status = cairo_matrix_invert (&mat); + if (status) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + cairo_pattern_set_matrix (&surface_pattern.base, &mat); + + x_advance = scaled_glyph->metrics.x_advance; + y_advance = scaled_glyph->metrics.y_advance; + font_matrix_inverse = scaled_font->font_matrix; + status = cairo_matrix_invert (&font_matrix_inverse); + if (status) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance); + *width = x_advance; + + *bbox = scaled_glyph->bbox; + _cairo_matrix_transform_bounding_box_fixed (&scaled_font->scale_inverse, + bbox, NULL); + + _cairo_output_stream_printf (surface->output, + "%f 0 d0\n", + x_advance); + + _cairo_pdf_surface_paint_surface_pattern (surface, + CAIRO_OPERATOR_OVER, + &surface_pattern.base, + &extents, + 1.0, /* alpha */ + NULL, /* smask_res */ + FALSE); /* mask */ + + cleanup: + cairo_surface_destroy (glyph_surface); + + return status; +} + +static cairo_int_status_t +cairo_pdf_surface_emit_color_glyph_image (cairo_pdf_surface_t *surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index, + cairo_box_t *bbox, + double *width) +{ + cairo_rectangle_int_t extents; + cairo_pattern_t *image_pattern; + cairo_scaled_glyph_t *scaled_glyph; + cairo_matrix_t mat; + cairo_int_status_t status, status2; + double x_advance, y_advance; + cairo_matrix_t font_matrix_inverse; + + _cairo_scaled_font_freeze_cache (scaled_font); + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + if (unlikely (status)) + goto FAIL; + + extents.x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x); + extents.y = _cairo_fixed_to_double (scaled_glyph->bbox.p1.y); + extents.width = _cairo_fixed_to_double (scaled_glyph->bbox.p2.x) - extents.x; + extents.height = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y) - extents.y; + + image_pattern = cairo_pattern_create_for_surface (&scaled_glyph->color_surface->base); + + cairo_matrix_init_translate (&mat, extents.x, extents.y); + cairo_matrix_multiply (&mat, &mat, &scaled_font->scale_inverse); + status2 = cairo_matrix_invert (&mat); + cairo_pattern_set_matrix (image_pattern, &mat); + + x_advance = scaled_glyph->metrics.x_advance; + y_advance = scaled_glyph->metrics.y_advance; + font_matrix_inverse = scaled_font->font_matrix; + status2 = cairo_matrix_invert (&font_matrix_inverse); + + /* The invertability of font_matrix is tested in + * pdf_operators_show_glyphs before any glyphs are mapped to the + * subset. */ + assert (status2 == CAIRO_INT_STATUS_SUCCESS); + + cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance); + *width = x_advance; + + *bbox = scaled_glyph->bbox; + _cairo_matrix_transform_bounding_box_fixed (&scaled_font->scale_inverse, + bbox, NULL); + + _cairo_output_stream_printf (surface->output, + "%f 0 d0\n", + x_advance); + + _cairo_pdf_surface_paint_surface_pattern (surface, + CAIRO_OPERATOR_OVER, + image_pattern, + &extents, + 1.0, /* alpha */ + NULL, /* smask_res */ + FALSE); /* mask */ + cairo_pattern_destroy (image_pattern); + FAIL: + _cairo_scaled_font_thaw_cache (scaled_font); + + return status; +} + +static cairo_int_status_t _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { @@ -6541,6 +6811,20 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, font_subset->glyphs[i], &bbox, &widths[i]); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = cairo_pdf_surface_emit_color_glyph (surface, + font_subset->scaled_font, + font_subset->glyphs[i], + &bbox, + &widths[i]); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = cairo_pdf_surface_emit_color_glyph_image (surface, + font_subset->scaled_font, + font_subset->glyphs[i], + &bbox, + &widths[i]); + } + } if (unlikely (status)) break; @@ -6725,12 +7009,6 @@ _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, _cairo_pdf_surface_emit_scaled_font_subset, surface); - if (unlikely (status)) - goto BAIL; - - status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_pdf_surface_emit_scaled_font_subset, - surface); BAIL: _cairo_scaled_font_subsets_destroy (surface->font_subsets); @@ -7627,6 +7905,9 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, /* The SOURCE operator is supported if the pattern is opaque or if * there is nothing painted underneath. */ if (op == CAIRO_OPERATOR_SOURCE) { + if (surface->type3_replay) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; @@ -8762,6 +9043,13 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, return status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + /* Enabling text in Type 3 fonts currently crashes cairo. Most + * PDF viewers don't seem to suport text in Type 3 so we let + * this go to image fallback. + */ + if (surface->type3_replay) + return CAIRO_INT_STATUS_UNSUPPORTED; + status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup; } @@ -8915,6 +9203,68 @@ _cairo_pdf_surface_tag (void *abstract_surface, return status; } +/* The Type 3 font subset support will the embed the + * CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE image if vector operations + * are not supported. The only case we don't currently handle is if a + * foreground color is used. + */ +static cairo_bool_t +_cairo_pdf_surface_supports_color_glyph (void *abstract_surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_color_glyph_t glyph_key; + cairo_pdf_color_glyph_t *glyph_entry; + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + + glyph_key.scaled_font = scaled_font; + glyph_key.glyph_index = glyph_index; + + _cairo_pdf_color_glyph_init_key (&glyph_key); + glyph_entry = _cairo_hash_table_lookup (surface->color_glyphs, &glyph_key.base); + if (glyph_entry) + return glyph_entry->supported; + + glyph_entry = _cairo_malloc (sizeof (cairo_pdf_color_glyph_t)); + if (glyph_entry == NULL) { + status = _cairo_surface_set_error (&surface->base, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + return FALSE; + } + + glyph_entry->scaled_font = cairo_scaled_font_reference (scaled_font); + glyph_entry->glyph_index = glyph_index; + _cairo_pdf_color_glyph_init_key (glyph_entry); + + glyph_entry->supported = FALSE; + _cairo_scaled_font_freeze_cache (scaled_font); + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + if (unlikely (status)) + goto done; + + glyph_entry->supported = !(scaled_glyph->recording_uses_foreground_color || + scaled_glyph->recording_uses_foreground_marker); + + done: + _cairo_scaled_font_thaw_cache (scaled_font); + + status = _cairo_hash_table_insert (surface->color_glyphs, + &glyph_entry->base); + if (unlikely(status)) { + status = _cairo_surface_set_error (&surface->base, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + return FALSE; + } + + return glyph_entry->supported; +} + static cairo_int_status_t _cairo_pdf_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) @@ -8973,6 +9323,7 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_show_text_glyphs, _cairo_pdf_surface_get_supported_mime_types, _cairo_pdf_surface_tag, + _cairo_pdf_surface_supports_color_glyph, }; static const cairo_paginated_surface_backend_t diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index a37af4c5a..abc9407ee 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -912,18 +912,11 @@ _cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface) if (unlikely (status)) return status; - status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, - _cairo_ps_surface_emit_scaled_font_subset, - surface); - if (unlikely (status)) - return status; - - return _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_ps_surface_emit_scaled_font_subset, - surface); + return _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, + _cairo_ps_surface_emit_scaled_font_subset, + surface); } - static cairo_int_status_t _cairo_ps_surface_emit_forms (cairo_ps_surface_t *surface) { diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c index e87320523..158ea16ba 100644 --- a/src/cairo-recording-surface.c +++ b/src/cairo-recording-surface.c @@ -1231,6 +1231,14 @@ _cairo_recording_surface_tag (void *abstract_surface, return status; } +static cairo_bool_t +_cairo_recording_surface_supports_color_glyph (void *abstract_surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + return TRUE; +} + static void _command_init_copy (cairo_recording_surface_t *surface, cairo_command_header_t *dst, @@ -1703,6 +1711,7 @@ static const cairo_surface_backend_t cairo_recording_surface_backend = { _cairo_recording_surface_show_text_glyphs, NULL, /* get_supported_mime_types */ _cairo_recording_surface_tag, + _cairo_recording_surface_supports_color_glyph, }; static unsigned int diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h index 5b531d298..4ba7cbee5 100644 --- a/src/cairo-scaled-font-subsets-private.h +++ b/src/cairo-scaled-font-subsets-private.h @@ -297,41 +297,6 @@ _cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t void *closure); /** - * _cairo_scaled_font_subsets_foreach_user: - * @font_subsets: a #cairo_scaled_font_subsets_t - * @font_subset_callback: a function to be called for each font subset - * @closure: closure data for the callback function - * - * Iterate over each unique scaled font subset as created by calls to - * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by - * unique pairs of (font_id, subset_id) as returned by - * _cairo_scaled_font_subsets_map_glyph(). - * - * For each subset, @font_subset_callback will be called and will be - * provided with both a #cairo_scaled_font_subset_t object containing - * all the glyphs in the subset as well as the value of @closure. - * - * The #cairo_scaled_font_subset_t object contains the scaled_font, - * the font_id, and the subset_id corresponding to all glyphs - * belonging to the subset. In addition, it contains an array providing - * a mapping between subset glyph indices and the original scaled font - * glyph indices. - * - * The index of the array corresponds to subset_glyph_index values - * returned by _cairo_scaled_font_subsets_map_glyph() while the - * values of the array correspond to the scaled_font_glyph_index - * values passed as input to the same function. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero - * value indicating an error. Possible errors include - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure); - -/** * _cairo_scaled_font_subset_create_glyph_names: * @font_subsets: a #cairo_scaled_font_subsets_t * diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c index c5ba12665..2a9e8144c 100644 --- a/src/cairo-scaled-font-subsets.c +++ b/src/cairo-scaled-font-subsets.c @@ -62,7 +62,6 @@ typedef enum { typedef enum { CAIRO_SUBSETS_FOREACH_UNSCALED, CAIRO_SUBSETS_FOREACH_SCALED, - CAIRO_SUBSETS_FOREACH_USER } cairo_subsets_foreach_type_t; typedef struct _cairo_sub_font { @@ -70,7 +69,6 @@ typedef struct _cairo_sub_font { cairo_bool_t is_scaled; cairo_bool_t is_composite; - cairo_bool_t is_user; cairo_bool_t use_latin_subset; cairo_bool_t reserve_notdef; cairo_scaled_font_subsets_t *parent; @@ -284,8 +282,7 @@ _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, sub_font->is_scaled = is_scaled; sub_font->is_composite = is_composite; - sub_font->is_user = _cairo_font_face_is_user (scaled_font->font_face); - sub_font->reserve_notdef = !sub_font->is_user; + sub_font->reserve_notdef = !sub_font->is_scaled; _cairo_sub_font_init_key (sub_font, scaled_font); sub_font->parent = parent; @@ -295,7 +292,7 @@ _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, sub_font->use_latin_subset = parent->use_latin_subset; /* latin subsets of Type 3 and CID CFF fonts are not supported */ - if (sub_font->is_user || sub_font->is_scaled || + if (sub_font->is_scaled || _cairo_cff_scaled_font_is_cid_cff (scaled_font) ) { sub_font->use_latin_subset = FALSE; @@ -620,12 +617,11 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, } } - /* If glyph is in the winansi encoding and font is not a user + /* If glyph is in the winansi encoding and font is not a scaled * font, put glyph in the latin subset. */ is_latin = FALSE; latin_character = -1; - if (sub_font->use_latin_subset && - (! _cairo_font_face_is_user (sub_font->scaled_font->font_face))) + if (sub_font->use_latin_subset && !sub_font->is_scaled) { latin_character = _cairo_unicode_to_winansi (font_unicode); if (latin_character > 0) @@ -839,6 +835,9 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, cairo_int_status_t status; int max_glyphs; cairo_bool_t type1_font; + cairo_bool_t has_path; + cairo_bool_t has_color; + cairo_bool_t is_user; /* Lookup glyph in unscaled subsets */ if (subsets->type != CAIRO_SUBSETS_SCALED) { @@ -872,30 +871,47 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, /* Glyph not found. Determine whether the glyph is outline or * bitmap and add to the appropriate subset. - * - * glyph_index 0 (the .notdef glyph) is a special case. Some fonts + */ + is_user = _cairo_font_face_is_user (scaled_font->font_face); + _cairo_scaled_font_freeze_cache (scaled_font); + /* Check if glyph is color */ + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + has_color = (status == CAIRO_INT_STATUS_SUCCESS); + + /* Check if glyph has a path */ + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_PATH, + NULL, /* foreground color */ + &scaled_glyph); + has_path = (status == CAIRO_INT_STATUS_SUCCESS); + + /* glyph_index 0 (the .notdef glyph) is a special case. Some fonts * will return CAIRO_INT_STATUS_UNSUPPORTED when doing a * _scaled_glyph_lookup(_GLYPH_INFO_PATH). Type1-fallback creates * empty glyphs in this case so we can put the glyph in a unscaled - * subset. */ - if (scaled_font_glyph_index == 0 || - _cairo_font_face_is_user (scaled_font->font_face)) { - status = CAIRO_STATUS_SUCCESS; - } else { - _cairo_scaled_font_freeze_cache (scaled_font); - status = _cairo_scaled_glyph_lookup (scaled_font, - scaled_font_glyph_index, - CAIRO_SCALED_GLYPH_INFO_PATH, + * subset. + */ + if (scaled_font_glyph_index == 0 && !is_user) + has_path = TRUE; + + /* If this fails there is nothing we can do with this glyph. */ + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, NULL, /* foreground color */ - &scaled_glyph); - _cairo_scaled_font_thaw_cache (scaled_font); - } + &scaled_glyph); + _cairo_scaled_font_thaw_cache (scaled_font); if (_cairo_int_status_is_error (status)) return status; - if (status == CAIRO_INT_STATUS_SUCCESS && - subsets->type != CAIRO_SUBSETS_SCALED && - ! _cairo_font_face_is_user (scaled_font->font_face)) + /* Type 3 glyphs (is_user and has_color) must be added to scaled subset */ + if (subsets->type != CAIRO_SUBSETS_SCALED && + has_path && !has_color && !is_user) { /* Path available. Add to unscaled subset. */ key.is_scaled = FALSE; @@ -1007,19 +1023,12 @@ _cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t { cairo_sub_font_collection_t collection; cairo_sub_font_t *sub_font; - cairo_bool_t is_scaled, is_user; + cairo_bool_t is_scaled; is_scaled = FALSE; - is_user = FALSE; - if (type == CAIRO_SUBSETS_FOREACH_USER) - is_user = TRUE; - - if (type == CAIRO_SUBSETS_FOREACH_SCALED || - type == CAIRO_SUBSETS_FOREACH_USER) - { + if (type == CAIRO_SUBSETS_FOREACH_SCALED) is_scaled = TRUE; - } if (is_scaled) collection.glyphs_size = font_subsets->max_glyphs_per_scaled_subset_used; @@ -1055,9 +1064,7 @@ _cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t sub_font = font_subsets->unscaled_sub_fonts_list; while (sub_font) { - if (sub_font->is_user == is_user) - _cairo_sub_font_collect (sub_font, &collection); - + _cairo_sub_font_collect (sub_font, &collection); sub_font = sub_font->next; } free (collection.utf8); @@ -1090,17 +1097,6 @@ _cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *fo CAIRO_SUBSETS_FOREACH_UNSCALED); } -cairo_status_t -_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure) -{ - return _cairo_scaled_font_subsets_foreach_internal (font_subsets, - font_subset_callback, - closure, - CAIRO_SUBSETS_FOREACH_USER); -} - static cairo_bool_t _cairo_string_equal (const void *key_a, const void *key_b) { diff --git a/src/cairo-surface-backend-private.h b/src/cairo-surface-backend-private.h index d31655be8..15032de20 100644 --- a/src/cairo-surface-backend-private.h +++ b/src/cairo-surface-backend-private.h @@ -124,14 +124,14 @@ struct _cairo_surface_backend { (*paint) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, - const cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*mask) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - const cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*stroke) (void *surface, @@ -143,7 +143,7 @@ struct _cairo_surface_backend { const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - const cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*fill) (void *surface, @@ -153,7 +153,7 @@ struct _cairo_surface_backend { cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - const cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*fill_stroke) (void *surface, @@ -196,7 +196,7 @@ struct _cairo_surface_backend { int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip); + const cairo_clip_t *clip); const char ** (*get_supported_mime_types) (void *surface); @@ -207,6 +207,10 @@ struct _cairo_surface_backend { const char *tag_name, const char *attributes); + cairo_bool_t + (*supports_color_glyph) (void *surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index); }; cairo_private cairo_status_t diff --git a/src/cairo-surface.c b/src/cairo-surface.c index e9e6233a6..609eb9ccf 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -2795,9 +2795,21 @@ composite_color_glyphs (cairo_surface_t *surface, goto UNLOCK; if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) != 0) { - skip_cluster = FALSE; - break; - } + cairo_bool_t supports_color_glyph = FALSE; + + if (surface->backend->supports_color_glyph) { + _cairo_scaled_font_thaw_cache (scaled_font); + supports_color_glyph = _cairo_surface_supports_color_glyph (surface, scaled_font, glyphs[gp].index); + + memset (glyph_cache, 0, sizeof (glyph_cache)); + _cairo_scaled_font_freeze_cache (scaled_font); + } + + if (!supports_color_glyph) { + skip_cluster = FALSE; + break; + } + } } if (skip_cluster) { @@ -2967,9 +2979,9 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, if (num_glyphs == 0) goto DONE; - } - else + } else { utf8_copy = NULL; + } /* The logic here is duplicated in _cairo_analysis_surface show_glyphs and * show_text_glyphs. Keep in synch. */ @@ -3057,6 +3069,16 @@ _cairo_surface_tag (cairo_surface_t *surface, return _cairo_surface_set_error (surface, status); } +cairo_bool_t +_cairo_surface_supports_color_glyph (cairo_surface_t *surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + if (surface->backend->supports_color_glyph != NULL) + return surface->backend->supports_color_glyph (surface, scaled_font, glyph_index); + + return FALSE; +} /** * _cairo_surface_set_resolution: diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index b39a94a37..b7212a547 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -1555,14 +1555,6 @@ _cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document) status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets, _cairo_svg_document_emit_font_subset, document); - if (unlikely (status)) - goto FAIL; - - status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets, - _cairo_svg_document_emit_font_subset, - document); - - FAIL: _cairo_scaled_font_subsets_destroy (document->font_subsets); document->font_subsets = NULL; diff --git a/src/cairo-type3-glyph-surface.c b/src/cairo-type3-glyph-surface.c index c0971e102..53c029493 100644 --- a/src/cairo-type3-glyph-surface.c +++ b/src/cairo-type3-glyph-surface.c @@ -457,12 +457,22 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, _cairo_type3_glyph_surface_set_stream (surface, stream); _cairo_scaled_font_freeze_cache (surface->scaled_font); + + /* Check if this is a color glyph and bail out if it is */ status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS | - CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, NULL, /* foreground color */ &scaled_glyph); + if (status == CAIRO_INT_STATUS_SUCCESS) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; + } status = _cairo_scaled_glyph_lookup (surface->scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c index e7a1d0211..ee11d864c 100644 --- a/src/cairo-user-font.c +++ b/src/cairo-user-font.c @@ -183,7 +183,9 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon } else { status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; - if (face->scaled_font_methods.render_color_glyph) { + if (face->scaled_font_methods.render_color_glyph && + scaled_font->base.options.color_mode != CAIRO_COLOR_MODE_NO_COLOR) + { recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, TRUE, foreground_color); cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface, TRUE); diff --git a/src/cairoint.h b/src/cairoint.h index 1e8c02fb3..5ce747ee4 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -406,6 +406,10 @@ _cairo_hash_bytes (uintptr_t hash, const void *bytes, unsigned int length); +cairo_private uintptr_t +_cairo_hash_uintptr (uintptr_t hash, + uintptr_t u); + /* We use bits 24-27 to store phases for subpixel positions */ #define _cairo_scaled_glyph_index(g) ((unsigned long)((g)->hash_entry.hash & 0xffffff)) #define _cairo_scaled_glyph_xphase(g) (int)(((g)->hash_entry.hash >> 24) & 3) @@ -1483,6 +1487,11 @@ _cairo_surface_tag (cairo_surface_t *surface, const char *tag_name, const char *attributes); +cairo_private cairo_bool_t +_cairo_surface_supports_color_glyph (cairo_surface_t *surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index); + cairo_private cairo_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, |