diff --git a/.gitlab-ci/ignore-svg11-argb32.txt b/.gitlab-ci/ignore-svg11-argb32.txt index 2f53dd340..71365a16e 100644 --- a/.gitlab-ci/ignore-svg11-argb32.txt +++ b/.gitlab-ci/ignore-svg11-argb32.txt @@ -49,4 +49,3 @@ text-rotate text-unhinted-metrics tighten-bounds unbounded-operator -user-font-color diff --git a/.gitlab-ci/ignore-svg11-rgb24.txt b/.gitlab-ci/ignore-svg11-rgb24.txt index 44ddbff0e..d78bfa26f 100644 --- a/.gitlab-ci/ignore-svg11-rgb24.txt +++ b/.gitlab-ci/ignore-svg11-rgb24.txt @@ -28,7 +28,6 @@ filter-bilinear-extents filter-nearest-offset filter-nearest-transformed finer-grained-fallbacks -ft-color-font ft-show-glyphs-positioning ft-text-vertical-layout-type1 ft-text-vertical-layout-type3 @@ -106,4 +105,3 @@ tighten-bounds unbounded-operator xcb-surface-source xlib-surface-source -user-font-color diff --git a/src/cairo-svg-surface-private.h b/src/cairo-svg-surface-private.h index e2a194081..72820f152 100644 --- a/src/cairo-svg-surface-private.h +++ b/src/cairo-svg-surface-private.h @@ -45,6 +45,13 @@ #include "cairo-surface-private.h" +typedef struct _cairo_svg_color_glyph { + cairo_hash_entry_t base; + cairo_scaled_font_t *scaled_font; + unsigned long glyph_index; + cairo_bool_t supported; +} cairo_svg_color_glyph_t; + struct _cairo_svg_surface_start { cairo_surface_t base; diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 249f46f53..dbff5a19f 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -117,6 +117,25 @@ _cairo_svg_source_surface_equal (const void *key_a, const void *key_b) return a->id == b->id; } +static cairo_bool_t +_cairo_svg_color_glyph_equal (const void *key_a, const void *key_b) +{ + const cairo_svg_color_glyph_t *a = key_a; + const cairo_svg_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_svg_color_glyph_init_key (cairo_svg_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 void _cairo_svg_source_surface_pluck (void *entry, void *closure) { @@ -128,6 +147,18 @@ _cairo_svg_source_surface_pluck (void *entry, void *closure) free (source_surface); } +static void +_cairo_svg_color_glyph_pluck (void *entry, void *closure) +{ + cairo_svg_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 void _cairo_svg_paint_init_key (cairo_svg_paint_t *paint) { @@ -526,6 +557,8 @@ typedef struct _cairo_svg_surface { cairo_svg_document_t *document; + cairo_hash_table_t *color_glyphs; + cairo_svg_stream_t xml_node; cairo_array_t page_set; @@ -1080,13 +1113,19 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, surface->document = _cairo_svg_document_reference (document); + surface->color_glyphs = _cairo_hash_table_create (_cairo_svg_color_glyph_equal); + if (unlikely (surface->color_glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP1; + } + surface->xml_node = _cairo_svg_stream_create (); _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t)); surface->source_surfaces = _cairo_hash_table_create (_cairo_svg_source_surface_equal); if (unlikely (surface->source_surfaces == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; + goto CLEANUP2; } _cairo_surface_clipper_init (&surface->clipper, _cairo_svg_surface_clipper_intersect_clip_path); @@ -1110,7 +1149,9 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, } /* ignore status as we are on the error path */ - CLEANUP: +CLEANUP2: + (void) _cairo_hash_table_destroy (surface->color_glyphs); +CLEANUP1: (void) _cairo_svg_stream_destroy (&surface->xml_node); (void) _cairo_svg_document_destroy (document); @@ -1346,6 +1387,59 @@ _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document, return status; } +static cairo_int_status_t +_cairo_svg_document_emit_color_glyph_data (cairo_svg_document_t *document, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + cairo_status_t status; + + cairo_scaled_glyph_t *scaled_glyph; + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + if (unlikely (status)) { + return status; + } + // User fonts always have a recording surface, but they may not be color glyphs. + if (scaled_glyph->color_glyph_set && !scaled_glyph->color_glyph) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_surface_t *paginated_surface = _cairo_svg_surface_create_for_document (document, + CAIRO_CONTENT_COLOR_ALPHA, + 0, + 0, + FALSE); + cairo_svg_surface_t *svg_surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (paginated_surface); + status = paginated_surface->status; + if (unlikely (status)) { + goto cleanup; + } + + cairo_surface_set_fallback_resolution (paginated_surface, + document->owner->x_fallback_resolution, + document->owner->y_fallback_resolution); + + cairo_pattern_t *pattern = cairo_pattern_create_for_surface (scaled_glyph->recording_surface); + _cairo_svg_surface_emit_composite_pattern (&document->xml_node_glyphs, + svg_surface, + (cairo_surface_pattern_t *) pattern, + invalid_pattern_id, + NULL); + cairo_pattern_destroy (pattern); + +cleanup: + if (status == CAIRO_STATUS_SUCCESS) { + status = cairo_surface_status (paginated_surface); + } + cairo_surface_destroy (paginated_surface); + + return status; +} + static cairo_int_status_t _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, cairo_scaled_font_t *scaled_font, @@ -1483,7 +1577,7 @@ _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, goto cleanup; } - cleanup: +cleanup: if (status == CAIRO_STATUS_SUCCESS) { status = cairo_surface_status (paginated_surface); } @@ -1510,13 +1604,19 @@ _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, font_id, subset_glyph_index); - status = _cairo_svg_document_emit_outline_glyph_data (document, - scaled_font, - scaled_font_glyph_index); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - status = _cairo_svg_document_emit_bitmap_glyph_data (document, - scaled_font, - scaled_font_glyph_index); + status = _cairo_svg_document_emit_color_glyph_data (document, + scaled_font, + scaled_font_glyph_index); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_svg_document_emit_outline_glyph_data (document, + scaled_font, + scaled_font_glyph_index); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_svg_document_emit_bitmap_glyph_data (document, + scaled_font, + scaled_font_glyph_index); + } + } if (unlikely (status)) return status; @@ -1660,6 +1760,11 @@ _cairo_svg_surface_finish (void *abstract_surface) _cairo_hash_table_foreach (surface->source_surfaces, _cairo_svg_source_surface_pluck, surface->source_surfaces); _cairo_hash_table_destroy (surface->source_surfaces); + _cairo_hash_table_foreach (surface->color_glyphs, + _cairo_svg_color_glyph_pluck, + surface->color_glyphs); + _cairo_hash_table_destroy (surface->color_glyphs); + status = _cairo_svg_document_destroy (surface->document); if (final_status == CAIRO_STATUS_SUCCESS) { final_status = status; @@ -2430,6 +2535,28 @@ _cairo_svg_surface_emit_composite_recording_pattern (cairo_svg_stream_t *output, return status; } + // FIXME error handling order + cairo_svg_paint_t *paint_entry = _cairo_calloc (sizeof (cairo_svg_paint_t)); + if (paint_entry == NULL) { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; + // how to get extents ??? + is_bounded =_cairo_surface_get_extents (pattern->surface, &extents); + //assert (is_bounded); ??? + paint_entry->source_id = surface->source_id; + paint_entry->box.p1.x = extents.x; + paint_entry->box.p1.y = extents.y; + paint_entry->box.p2.x = extents.x + extents.width; + paint_entry->box.p2.y = extents.y + extents.height; + _cairo_svg_paint_box_add_padding (&paint_entry->box); + + _cairo_array_init (&paint_entry->paint_elements, sizeof (cairo_svg_paint_element_t)); + _cairo_svg_paint_init_key (paint_entry); + status = _cairo_hash_table_insert (document->paints, &paint_entry->base); + surface->transitive_paint_used = TRUE; } @@ -4082,14 +4209,25 @@ _cairo_svg_surface_show_glyphs_impl (cairo_svg_stream_t *output, return status; } +static cairo_bool_t +_cairo_svg_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + static cairo_int_status_t -_cairo_svg_surface_show_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip) +_cairo_svg_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -4126,6 +4264,63 @@ _cairo_svg_surface_get_supported_mime_types (void *abstract_surface) return _cairo_svg_supported_mime_types; } +static cairo_bool_t +_cairo_svg_surface_supports_color_glyph (void *abstract_surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + cairo_svg_surface_t *surface = abstract_surface; + cairo_svg_color_glyph_t glyph_key; + cairo_svg_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_svg_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_svg_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_svg_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_RECORDING_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 const cairo_surface_backend_t cairo_svg_surface_backend = { CAIRO_SURFACE_TYPE_SVG, _cairo_svg_surface_finish, @@ -4156,10 +4351,14 @@ static const cairo_surface_backend_t cairo_svg_surface_backend = { _cairo_svg_surface_stroke, _cairo_svg_surface_fill, _cairo_svg_surface_fill_stroke, - _cairo_svg_surface_show_glyphs, - NULL, /* has_show_text_glyphs */ - NULL, /* show_text_glyphs */ + NULL, /* show_glyphs */ + _cairo_svg_surface_has_show_text_glyphs, + _cairo_svg_surface_show_text_glyphs, _cairo_svg_surface_get_supported_mime_types, + NULL, /* tag */ + _cairo_svg_surface_supports_color_glyph, + NULL, /* analyze_recording_surface */ + NULL, /* command_id */ }; static cairo_status_t diff --git a/test/reference/ft-color-font.svg11.argb32.ref.png b/test/reference/ft-color-font.svg11.argb32.ref.png new file mode 100644 index 000000000..4cda8a3ce Binary files /dev/null and b/test/reference/ft-color-font.svg11.argb32.ref.png differ diff --git a/test/reference/ft-color-font.svg11.rgb24.ref.png b/test/reference/ft-color-font.svg11.rgb24.ref.png new file mode 100644 index 000000000..af460285e Binary files /dev/null and b/test/reference/ft-color-font.svg11.rgb24.ref.png differ diff --git a/test/reference/ft-svg-render-color.svg.ref.png b/test/reference/ft-svg-render-color.svg.ref.png new file mode 100644 index 000000000..fcfdc5f4e Binary files /dev/null and b/test/reference/ft-svg-render-color.svg.ref.png differ diff --git a/test/reference/ft-svg-render-color.svg.rgb24.ref.png b/test/reference/ft-svg-render-color.svg.rgb24.ref.png deleted file mode 100644 index fd7d76786..000000000 Binary files a/test/reference/ft-svg-render-color.svg.rgb24.ref.png and /dev/null differ diff --git a/test/reference/ft-svg-render-doc.svg.ref.png b/test/reference/ft-svg-render-doc.svg.ref.png new file mode 100644 index 000000000..a22368980 Binary files /dev/null and b/test/reference/ft-svg-render-doc.svg.ref.png differ diff --git a/test/reference/ft-svg-render-doc.svg.rgb24.ref.png b/test/reference/ft-svg-render-doc.svg.rgb24.ref.png deleted file mode 100644 index 791feb909..000000000 Binary files a/test/reference/ft-svg-render-doc.svg.rgb24.ref.png and /dev/null differ diff --git a/test/reference/ft-svg-render-fill.svg.ref.png b/test/reference/ft-svg-render-fill.svg.ref.png new file mode 100644 index 000000000..685fcee07 Binary files /dev/null and b/test/reference/ft-svg-render-fill.svg.ref.png differ diff --git a/test/reference/ft-svg-render-fill.svg.rgb24.ref.png b/test/reference/ft-svg-render-fill.svg.rgb24.ref.png deleted file mode 100644 index ac892036a..000000000 Binary files a/test/reference/ft-svg-render-fill.svg.rgb24.ref.png and /dev/null differ diff --git a/test/reference/user-font-color.svg11.ref.png b/test/reference/user-font-color.svg11.ref.png new file mode 100644 index 000000000..55f4febf3 Binary files /dev/null and b/test/reference/user-font-color.svg11.ref.png differ