diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -526,18 +526,20 @@ _cairo_ft_unscaled_font_destroy (void *a _cairo_hash_table_remove (font_map->hash_table, &unscaled->base.hash_entry); if (unscaled->from_face) { /* See comments in _ft_font_face_destroy about the "zombie" state * for a _ft_font_face. */ - if (unscaled->faces && !unscaled->faces->unscaled) + if (unscaled->faces && !unscaled->faces->unscaled) { + assert (!unscaled->faces->next); cairo_font_face_destroy (&unscaled->faces->base); + } } else { _font_map_release_face_lock_held (font_map, unscaled); } unscaled->face = NULL; _cairo_ft_unscaled_font_map_unlock (); _cairo_ft_unscaled_font_fini (unscaled); @@ -2218,32 +2220,35 @@ _cairo_ft_font_face_destroy (void *abstr cairo_ft_font_face_t *font_face = abstract_face; cairo_ft_font_face_t *tmp_face = NULL; cairo_ft_font_face_t *last_face = NULL; if (font_face == NULL) return; - /* When destroying the face created by cairo_ft_font_face_create_for_ft_face, + /* When destroying a face created by cairo_ft_font_face_create_for_ft_face, * we have a special "zombie" state for the face when the unscaled font - * is still alive but there are no public references to the font face. + * is still alive but there are no other references to a font face with + * the same FT_Face. * * We go from: * * font_face ------> unscaled * <-....weak....../ * * To: * * font_face <------- unscaled */ if (font_face->unscaled && font_face->unscaled->from_face && + !font_face->next && + font_face->unscaled->faces == font_face && CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->unscaled->base.ref_count) > 1) { cairo_font_face_reference (&font_face->base); _cairo_unscaled_font_destroy (&font_face->unscaled->base); font_face->unscaled = NULL; return; @@ -2380,16 +2385,25 @@ _cairo_ft_font_face_create (cairo_ft_uns for (font_face = unscaled->faces, prev_font_face = &unscaled->faces; font_face; prev_font_face = &font_face->next, font_face = font_face->next) { if (font_face->ft_options.load_flags == ft_options->load_flags && font_face->ft_options.extra_flags == ft_options->extra_flags && cairo_font_options_equal (&font_face->ft_options.base, &ft_options->base)) { + if (!font_face->unscaled) { + /* Resurrect this "zombie" font_face (from + * _cairo_ft_font_face_destroy), switching its unscaled_font + * from owner to ownee. */ + font_face->unscaled = unscaled; + _cairo_unscaled_font_reference (&unscaled->base); + return &font_face->base; + } + if (font_face->base.status == CAIRO_STATUS_SUCCESS) return cairo_font_face_reference (&font_face->base); /* The font_face has been left in an error state, abandon it. */ *prev_font_face = font_face->next; break; } } @@ -2401,16 +2415,24 @@ _cairo_ft_font_face_create (cairo_ft_uns return (cairo_font_face_t *)&_cairo_font_face_nil; } font_face->unscaled = unscaled; _cairo_unscaled_font_reference (&unscaled->base); font_face->ft_options = *ft_options; + if (unscaled->faces && !unscaled->faces->unscaled) { + /* This "zombie" font_face (from _cairo_ft_font_face_destroy) + * is no longer needed. */ + assert (unscaled->from_face && !unscaled->faces->next); + cairo_font_face_destroy (&unscaled->faces->base); + unscaled->faces = NULL; + } + font_face->next = unscaled->faces; unscaled->faces = font_face; #if CAIRO_HAS_FC_FONT font_face->pattern = NULL; #endif _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend);