From fe133b0199a20580d885b83ad4eec6155b24f869 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 16 Feb 2007 13:42:04 +0100 Subject: [PATCH] adding cairo-freetype.h, cairo-freetype-private.h and cairo-freetype-state.c to the build --- src/Makefile.am | 4 +- src/cairo-freetype-private.h | 38 ++ src/cairo-freetype-state.c | 937 ++++++++++++++++++++++++++++++++++++++++++ src/cairo-freetype.h | 57 +++ 4 files changed, 1034 insertions(+), 2 deletions(-) create mode 100644 src/cairo-freetype-private.h create mode 100644 src/cairo-freetype-state.c create mode 100644 src/cairo-freetype.h diff --git a/src/Makefile.am b/src/Makefile.am index 4a24ac2..23573bd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -113,8 +113,8 @@ backend_pkgconfigs += cairo-directfb.pc endif if CAIRO_HAS_FT_FONT -libcairo_ft_headers = cairo-ft.h -libcairo_ft_sources = cairo-ft-font.c cairo-ft-private.h +libcairo_ft_headers = cairo-ft.h cairo-freetype.h cairo-fontconfig.h +libcairo_ft_sources = cairo-ft-font.c cairo-ft-private.h cairo-freetype-private.h cairo-freetype-state.c backend_pkgconfigs += cairo-ft.pc endif diff --git a/src/cairo-freetype-private.h b/src/cairo-freetype-private.h new file mode 100644 index 0000000..9279472 --- /dev/null +++ b/src/cairo-freetype-private.h @@ -0,0 +1,38 @@ +#ifndef _CAIRO_FREETYPE_PRIVATE_H +#define _CAIRO_FREETYPE_PRIVATE_H + +#include "cairo-freetype.h" +#include "cairoint.h" + +CAIRO_BEGIN_DECLS + +/* the FreeType font face holds either a (path,index) pair, or a foreign FT_Face + * object that was created by the client, and thus belongs to a different FT_Library + * instance. + * + * note that the foreign FT_Face is owned by the font_face object and will disappear + * with it. + */ +typedef struct _cairo_freetype_font_face_t { + cairo_font_face_t base; + const char* path; + int index; + FT_Face foreign_face; + FT_ULong load_flags; + +} cairo_freetype_font_face_t; + +cairo_private const cairo_font_face_backend_t _cairo_freetype_font_face_backend; + +/* dynamic cast to a freetype font face, or NULL */ +cairo_freetype_font_face_t* +_cairo_font_face_to_freetype (cairo_font_face_t *font_face); + + +cairo_private cairo_bool_t +_cairo_font_face_is_freetype (cairo_font_face_t *font_face); + +CAIRO_END_DECLS + + +#endif /* _CAIRO_FREETYPE_PRIVATE_H */ diff --git a/src/cairo-freetype-state.c b/src/cairo-freetype-state.c new file mode 100644 index 0000000..40b8c1f --- /dev/null +++ b/src/cairo-freetype-state.c @@ -0,0 +1,937 @@ +#include "cairo-freetype-private.h" +#include +#include +#include FT_SIZES_H + +/* technical explanation of what's going on here: + * + * because FT_Face and FT_Size objects can have very large sizes in the heap, + * we want to limit their numbers to a reasonable maximum, defined by the + * macros CAIRO_MAX_FT_FACES and CAIRO_MAX_FT_SIZES respectively + * + * The FreeType state object thus contains two MRU-ordered linked-lists that + * hold FT_Face and FT_Size objects, respectively. + * + * each cairo_mru_list_t contains a "used" and a "free" node list, each one being + * called a "head" to avoid ambiguities. Each node can be in one of these states + * + * INIT: the node is being initialized, but is not in any head yet + * FREE: the node is in the "free" head, its reference count is 0 + * USED: the node is in the "used" head, its reference count is > 0 + * DONE: the node is being finalized, it is not in any head + * + * the possible state transitions are: + * + * INIT -> FREE <-> USED + * | + * v + * DONE + */ + +#define CAIRO_MAX_FT_FACES 4 +#define CAIRO_MAX_FT_SIZES 8 + +typedef struct _cairo_mru_list cairo_mru_list_t; + +/* Generic MRU-ordered list processing + * list items are placed in a circular doubly-linked list + * each one is reference-counted and has a hash + */ +typedef struct _cairo_mru { + struct _cairo_mru *next; + struct _cairo_mru *prev; + unsigned int hash; + int ref_count; + cairo_mru_list_t *list; +} cairo_mru_t; + + + +/* a function used to initialize a new generic MRU list node + * 'init' is a dummy node containing the initialization parameters, + * if it returns an error, the caller will automatically call + * the corresponding cairo_mru_done_func_t + * + * note that all root cairo_mru_t fields like hash, ref_count and list + * are already set when this function is called. + */ +typedef cairo_status_t (*cairo_mru_init_func_t) (cairo_mru_t *node, + cairo_mru_t *init); + +/* this function is used to test wether a node corresponds to a given key + * which is also a cairo_mru_t, most of the time + */ +typedef cairo_bool_t (*cairo_mru_equal_func_t) (cairo_mru_t *node, + void *key); + +/* this function is used to indicate to a node that has just transited + * between the USED and FREE states, the node simply needs to read + * it's ref_count, which corresponds to its *target* state. + * + * this is useful to implement data dependences between nodes from + * different lists. For example, a cairo_ft_size node must reference + * its parent cairo_ft_face when it becomes USED, and unreference it + * when it becomes FREE + */ +typedef void (*cairo_mru_transit_func_t) (cairo_mru_t *node); + +/* a function used to finalize a given node's content + */ +typedef void (*cairo_mru_done_func_t) (cairo_mru_t *node); + + +typedef struct _cairo_mru_backend { + size_t node_size; + cairo_mru_equal_func_t node_equal; + cairo_mru_init_func_t node_init; + cairo_mru_done_func_t node_done; + cairo_mru_transit_func_t node_transit; + +} cairo_mru_backend_t; + + + +struct _cairo_mru_list { + cairo_mru_t *used_nodes; + cairo_mru_t *free_nodes; + int num_nodes; + int max_nodes; + void *param1; + void *param2; + cairo_mru_backend_t backend; +}; + + + + +static void +_cairo_mru_remove (cairo_mru_t **phead, + cairo_mru_t *node) +{ + cairo_mru_t *prev = node->prev; + cairo_mru_t *next = node->next; + + prev->next = next; + next->prev = prev; + + if (node == *phead) + *phead = (node == next) ? NULL : next; +} + + +static void +_cairo_mru_prepend (cairo_mru_t **phead, + cairo_mru_t *node) +{ + cairo_mru_t *first = *phead; + + if (first == NULL) { + node->next = node; + node->prev = node; + } else { + node->next = first; + node->prev = first->prev; + } + *phead = node; +} + + +static void +_cairo_mru_raise (cairo_mru_t **phead, + cairo_mru_t *node) +{ + cairo_mru_t *first = *phead; + + assert (node->ref_count != 0); + + if (node != first) { + cairo_mru_t *next = node->next; + cairo_mru_t *prev = node->prev; + + prev->next = next; + next->prev = prev; + + node->next = first; + node->prev = first->prev; + + *phead = node; + } +} + + +static void +_cairo_mru_list_free_node (cairo_mru_list_t *list, + cairo_mru_t *node) +{ + assert (node->ref_count == 0); /* assume FREE */ + list->backend.node_done (node); + free (node); +} + + +static cairo_mru_t* +_cairo_mru_list_new_node (cairo_mru_list_t *list, + cairo_mru_t *init) +{ + cairo_mru_backend_t *backend = &list->backend; + cairo_mru_t *node = calloc( backend->node_size, 1 ); + + if (node != NULL) { + node->hash = init->hash; + node->ref_count = 0; /* important !! */ + node->list = list; + + if ( backend->node_init (node, init) != 0 ) { + backend->node_done (node); + free (node); + node = NULL; + } + } + return node; +} + + + + +static void +_cairo_mru_list_init (cairo_mru_list_t *list, + int max_nodes, + void *param1, + void *param2, + const cairo_mru_backend_t *backend) +{ + list->used_nodes = NULL; + list->free_nodes = NULL; + list->num_nodes = 0; + list->max_nodes = max_nodes; + list->param1 = param1; + list->param2 = param2; + list->backend = backend[0]; +} + + + + +/* find a given node in the MRU list, or create one if needed + * note this doesn't increment the reference count of returned + * nodes, and that new nodes have a refcount of 0 + */ +static cairo_mru_t* +_cairo_mru_list_find (cairo_mru_list_t *list, + cairo_mru_t *key) +{ + cairo_mru_t *node, *first; + + /* first look in the used_nodes list */ + first = list->used_nodes; + if (first != NULL) { + node = first; + do + { + if (node->hash == key->hash && list->backend.node_equal (node, key)) + return node; + + node = node->next; + } + while (node != first); + } + + /* then in the free_nodes list */ + first = list->free_nodes; + if (first != NULL) { + node = first; + do + { + if (node->hash == key->hash && list->backend.node_equal (node, key)) + return node; + + node = node->next; + } + while (node != first); + } + + /* not found, we need to create a new node. If our list if full, + * recycle the last free node + */ + if (list->num_nodes >= list->max_nodes && list->free_nodes) { + node = list->free_nodes->prev; + _cairo_mru_remove (&list->free_nodes, node); + list->num_nodes--; + assert (node->ref_count == 0); + _cairo_mru_list_free_node (list, node); + } + + node = _cairo_mru_list_new_node (list, key); + if (node != NULL) { + _cairo_mru_prepend (&list->used_nodes, node); + list->num_nodes++; + } + return node; +} + + +static void +_cairo_mru_list_done (cairo_mru_list_t *list) +{ + assert (list->used_nodes == NULL); + + while (list->free_nodes) { + cairo_mru_t *node = list->free_nodes; + + _cairo_mru_remove (&list->free_nodes, node); + _cairo_mru_list_free_node (list, node); + } + + list->num_nodes = 0; + list->max_nodes = 0; + list->param1 = NULL; + list->param2 = NULL; +} + + + + +/* destroy all free nodes corresponding a to given selection */ +static void +_cairo_mru_list_remove_select (cairo_mru_list_t *list, + cairo_mru_equal_func_t equal_func, + void *equal_key) +{ + cairo_mru_t *first, *node; + + first = list->free_nodes; + if (first != NULL) { + node = first; + do + { + if ( (*equal_func) (node, (cairo_mru_t*)equal_key) ) { + _cairo_mru_remove (&list->free_nodes, node); + list->num_nodes--; + _cairo_mru_list_free_node (list, node); + } + node = node->next; + } + while (node != first); + } +} + + + +static void +_cairo_mru_ref (cairo_mru_t *node) +{ + if (node) { + cairo_mru_list_t *list = node->list; + + if (node->ref_count == 0) + { + _cairo_mru_remove (&list->free_nodes, node); + _cairo_mru_prepend (&list->used_nodes, node); + } + else if (node != list->used_nodes) + { + _cairo_mru_raise (&list->used_nodes, node); + } + + node->ref_count ++; + + if (list->backend.node_transit) + (*list->backend.node_transit) (node); + } +} + + +static void +_cairo_mru_unref (cairo_mru_t *node) +{ + if (node) { + cairo_mru_list_t *list = node->list; + + if (--node->ref_count == 0) { + _cairo_mru_remove (&list->used_nodes, node); + + /* if our list if full, destroy the last free node */ + if (list->num_nodes > list->max_nodes && list->free_nodes) { + cairo_mru_t *last = list->free_nodes->prev; + + _cairo_mru_remove (&list->free_nodes, last); + _cairo_mru_list_free_node (list, last); + } + + _cairo_mru_prepend (&list->free_nodes, node); + + if (list->backend.node_transit) + (*list->backend.node_transit) (node); + } + } +} + + + + + +/* ft_face_node, each nodes holds a single FT_Face + * corresponding to a given (path,index) pair + * + * the FT_Library is accessed as node->list->param1 + * the list of corresponding cairo_ft_sizes as node->list->param2 + */ +typedef struct _cairo_ft_face { + cairo_mru_t base; + FT_Face face; + const char* path; + int index; + cairo_bool_t free_size; + +} cairo_ft_face_t; + +#define _cairo_ft_face_ref(x) _cairo_mru_ref ((cairo_mru_t*)(x)) +#define _cairo_ft_face_unref(x) _cairo_mru_unref ((cairo_mru_t*)(x)) + +struct _cairo_ft_size; + + +static void +_cairo_ft_face_hash (cairo_ft_face_t *node) +{ + unsigned int h; + + h = _cairo_hash_string (node->path); + h += node->index; + + node->base.hash = h; +} + + +static cairo_bool_t +_cairo_ft_face_equal (cairo_ft_face_t *node, + cairo_ft_face_t *key) +{ + return ( !strcmp (node->path, key->path) && node->index == key->index); +} + +/* this is a helper function used to remove all cairo_ft_size nodes that + * correspond to a given cairo_ft_face + */ +static cairo_bool_t _cairo_ft_size_equal_face (cairo_mru_t *size_node, + void *face_node); + +static void +_cairo_ft_face_done (cairo_ft_face_t *node) +{ + if (node->face) { + /* remove all cairo_ft_size corresponding to this cairo_ft_face */ + cairo_mru_list_t *sizes = node->base.list->param2; + + _cairo_mru_list_remove_select (sizes, &_cairo_ft_size_equal_face, node); + + FT_Done_Face (node->face); + node->face = NULL; + } + + if (node->path) { + free ((char*)node->path); + node->path = NULL; + } +} + + +static cairo_status_t +_cairo_ft_face_init (cairo_ft_face_t *node, + cairo_ft_face_t *key) +{ + FT_Library library = node->base.list->param1; + + node->path = strdup (key->path); + node->index = key->index; + + if (node->path == NULL) + return CAIRO_STATUS_NO_MEMORY; + + if ( FT_New_Face (library, node->path, node->index, &node->face) != 0 ) + return CAIRO_STATUS_NO_MEMORY; + + /* each new FT_Face created by FT_New_Face has a default FT_Size accessible + * as face->size. We set a flag to record this in the face node. the first + * cairo_ft_size that gets created will grab the FT_Size automatically + */ + node->free_size = 1; + + return 0; +} + + +static void +_cairo_mru_faces_list_init (cairo_mru_list_t *list, + FT_Library library, + cairo_mru_list_t *sizes_list) +{ + cairo_mru_backend_t backend; + + backend.node_size = sizeof(cairo_ft_face_t); + backend.node_equal = (cairo_mru_equal_func_t) _cairo_ft_face_equal; + backend.node_init = (cairo_mru_init_func_t) _cairo_ft_face_init; + backend.node_done = (cairo_mru_done_func_t) _cairo_ft_face_done; + backend.node_transit = NULL; + + _cairo_mru_list_init (list, CAIRO_MAX_FT_FACES, library, sizes_list, &backend); +} + + + +/* cairo_ft_size, holds a single FT_Size corresponding to either + * a (face,pixel_width,pixel_height) or a (face,char_width,char_height,x_dpi,y_dpi) + * tuple + * + * note that we don't put other cairo transforms in there, since these should be + * applied on-demand on 'used' objects. + */ + +typedef struct _cairo_ft_size { + cairo_mru_t base; + cairo_ft_face_t *face; + FT_Size size; + int width; + int height; + cairo_bool_t pixel_sizes; + int x_dpi; + int y_dpi; + cairo_mru_list_t *faces_list; + +} cairo_ft_size_t; + +#define _cairo_ft_size_ref(x) _cairo_mru_ref ((cairo_mru_t*)(x)) +#define _cairo_ft_size_unref(x) _cairo_mru_unref ((cairo_mru_t*)(x)) + +static void +_cairo_ft_size_hash (cairo_ft_size_t *node) +{ + unsigned int h; + + h = node->face->base.hash; + h += node->width + (node->height*31); + if (!node->pixel_sizes) { + h += 511*node->x_dpi; + h ^= node->y_dpi; + } + node->base.hash = h; +} + + +static cairo_bool_t +_cairo_ft_size_equal (cairo_ft_size_t *node, + cairo_ft_size_t *key) +{ + if (node->face != key->face || node->width != key->width || + node->height != key->height || node->pixel_sizes != key->pixel_sizes) + return 0; + + if (!node->pixel_sizes) { + if (node->x_dpi != key->x_dpi || node->y_dpi != key->y_dpi) + return 0; + } + return 1; +} + + +/* since a cairo_ft_size holds a pointer to a cairo_ft_face, we need to + * handle the USED <=> FREE transitions by doing ref/unref on it + * + * also activate the size if it becomes USED. + */ +static void +_cairo_ft_size_transit (cairo_ft_size_t *node) +{ + if (node->base.ref_count == 0) + { /* USED => FREE */ + _cairo_ft_face_unref (node->face); + } + else if (node->base.ref_count == 1) + { /* FREE => USED */ + _cairo_ft_face_ref (node->face); + FT_Activate_Size (node->size); + } +} + + +/* helper function used to remove all cairo_ft_size_t objects relating + * the same parent cairo_ft_face_t. used in _cairo_ft_face_done + */ +static cairo_bool_t +_cairo_ft_size_equal_face (cairo_mru_t *_size_node, + void *_face_node) +{ + cairo_ft_size_t *node = (cairo_ft_size_t*) _size_node; + + return (node->face == (cairo_ft_face_t*) _face_node); +} + + +static void +_cairo_ft_size_done (cairo_ft_size_t *node) +{ + if (node->size != NULL) { + FT_Done_Size (node->size); + node->size = NULL; + } + node->face = NULL; +} + + +static cairo_status_t +_cairo_ft_size_init (cairo_ft_size_t *node, + cairo_ft_size_t *init) +{ + cairo_ft_face_t *face = init->face; + FT_Error error; + + node->face = init->face; + node->width = init->width; + node->height = init->height; + node->pixel_sizes = init->pixel_sizes; + + if (init->pixel_sizes != 0) { + node->x_dpi = 0; + node->y_dpi = 0; + } else { + node->x_dpi = init->x_dpi; + node->y_dpi = init->y_dpi; + } + + if (face->free_size != 0) + { + /* if this is the first cairo_ft_size for a fresh new FT_Face, + * we grab the default FT_Size that was automatically created by FT_New_Face + */ + face->free_size = 0; + node->size = face->face->size; + } + else + { + /* we need to create a new FT_Size for the parent FT_Face */ + error = FT_New_Size (face->face, &node->size); + if (error) + return CAIRO_STATUS_NO_MEMORY; + } + + FT_Activate_Size (node->size); + + if (init->pixel_sizes) + error = FT_Set_Pixel_Sizes (node->size->face, node->width, node->height); + else + error = FT_Set_Char_Size (node->size->face, node->width, node->height, node->x_dpi, node->y_dpi); + + return error ? CAIRO_STATUS_NO_MEMORY : 0; +} + + +static void +_cairo_mru_sizes_list_init (cairo_mru_list_t *list) +{ + cairo_mru_backend_t backend; + + backend.node_size = sizeof(cairo_ft_size_t); + backend.node_equal = (cairo_mru_equal_func_t) _cairo_ft_size_equal; + backend.node_init = (cairo_mru_init_func_t) _cairo_ft_size_init; + backend.node_done = (cairo_mru_done_func_t) _cairo_ft_size_done; + backend.node_transit = (cairo_mru_transit_func_t) _cairo_ft_size_transit; + + _cairo_mru_list_init (list, CAIRO_MAX_FT_SIZES, NULL, NULL, &backend); +} + + + +/* cairo_freetype_state_t + * + * holds a FT_Library, a MRU-list of cairo_ft_face and a MRU-list of cairo_ft_size + * all protected by a single mutex + * + */ + +/* this is used to record the list of FT_Size we created from foreign faces */ +typedef struct _cairo_freetype_size_ref { + struct _cairo_freetype_size_ref *next; + FT_Size size; +} cairo_freetype_size_ref_t; + + +struct _cairo_freetype_state +{ + FT_Library library; + cairo_mru_list_t faces; + cairo_mru_list_t sizes; + cairo_freetype_size_ref_t *foreign_sizes; +}; + + +CAIRO_MUTEX_DECLARE(_cairo_freetype_state_mutex); + +static cairo_freetype_state_t *_cairo_freetype_state; + + +static void +_cairo_freetype_state_create (void) +{ + cairo_freetype_state_t *state = calloc( sizeof(*state), 1 ); + + if (state == NULL) + return; + + if ( FT_Init_FreeType( &state->library ) != 0 ) { + free (state); + return; + } + + _cairo_mru_faces_list_init (&state->faces, state->library, &state->sizes); + _cairo_mru_sizes_list_init (&state->sizes); + + state->foreign_sizes = NULL; + + _cairo_freetype_state = state; +} + + +cairo_freetype_state_t* +cairo_freetype_state_lock (void) +{ + CAIRO_MUTEX_LOCK (_cairo_freetype_state_mutex); + + if (_cairo_freetype_state == NULL) { + _cairo_freetype_state_create (); + if (_cairo_freetype_state == NULL) { + CAIRO_MUTEX_UNLOCK (_cairo_freetype_state_mutex); + return NULL; + } + } + return _cairo_freetype_state; +} + + +static cairo_ft_face_t* +_cairo_freetype_state_get_ft_face (cairo_freetype_state_t *state, + cairo_freetype_font_face_t *font_face) +{ + cairo_ft_face_t *face_node; + cairo_ft_face_t key; + + key.path = font_face->path; + key.index = font_face->index; + + _cairo_ft_face_hash (&key); + + face_node = (cairo_ft_face_t*) _cairo_mru_list_find (&state->faces, (cairo_mru_t*)&key); + if (face_node != NULL) { + _cairo_ft_face_ref (face_node); + } + return face_node; +} + + +FT_Face +cairo_freetype_state_get_face (cairo_freetype_state_t *state, + cairo_font_face_t *_font_face) +{ + cairo_freetype_font_face_t *font_face = _cairo_font_face_to_freetype (_font_face); + cairo_ft_face_t *face_node; + + if (font_face == NULL) + return NULL; + + if (font_face->foreign_face) + return font_face->foreign_face; + else { + face_node = _cairo_freetype_state_get_ft_face (state, font_face); + if (face_node == NULL) + return NULL; + + _cairo_ft_face_ref (face_node); + return face_node->face; + } +} + + +FT_Size +cairo_freetype_state_get_size (cairo_freetype_state_t *state, + cairo_scaled_font_t *scaled_font) +{ + FT_Size size; + cairo_freetype_font_face_t *font_face; + + if (scaled_font == NULL) + return NULL; + + font_face = _cairo_font_face_to_freetype (scaled_font->font_face); + if (font_face == NULL) + return NULL; + + if (font_face->foreign_face) { + cairo_freetype_size_ref_t *foreign_size; + + foreign_size = malloc (sizeof(*foreign_size)); + if (foreign_size == NULL) + return NULL; + + if ( FT_New_Size (font_face->foreign_face, &size) != 0 ) { + free (foreign_size); + return NULL; + } + + foreign_size->size = size; + foreign_size->next = state->foreign_sizes; + state->foreign_sizes = foreign_size; + + FT_Activate_Size (size); + return size; + } else { + cairo_ft_face_t *face_node; + cairo_ft_size_t *size_node; + cairo_ft_size_t key; + + face_node = _cairo_freetype_state_get_ft_face (state, font_face); + if (face_node == NULL) + return NULL; + + key.face = face_node; + + /* XXX: we recompute everything on this one, but these numbers should */ + /* really be stored in the cairo_ft_scaled_font instead !!! */ + { + double x_scale, y_scale; + cairo_bool_t have_shape; + + have_shape = cairo_freetype_compute_transform ( &scaled_font->scale, &x_scale, &y_scale, NULL, NULL ); + + if ( FT_IS_SCALABLE (face_node->face) ) + { + key.width = (int) x_scale*64.0; + key.height = (int) y_scale*64.0; + key.pixel_sizes = 0; + } else + { + /* for bitmap fonts, try to find the best size */ + FT_Face face = face_node->face; + double best_dist = DBL_MAX; + int best_width = 10, best_height = 10; + int i; + + for (i = 0; i < face->num_fixed_sizes; i++) + { +#ifdef HAVE_FT_BITMAP_SIZE_Y_PPEM + double size = face->available_sizes[i].y_ppem / 64; +#else + double size = face->available_sizes[i].height; +#endif + double dist = fabs (size - y_scale); + + if (dist < best_dist) { +#ifdef HAVE_FT_BITMAP_SIZE_Y_PPEM + best_width = face->available_sizes[i].x_ppem; + best_height = face->available_sizes[i].y_ppem; +#else + best_width = face->available_sizes[i].width; + best_height = face->available_sizes[i].height; +#endif + best_dist = dist; + } + } + + key.width = best_width; + key.height = best_height; +#ifdef HAVE_FT_BITMAP_SIZE_Y_PPEM + key.pixel_sizes = 0; +#else + key.pixel_sizes = 1; +#endif + } + } + key.x_dpi = 0; + key.y_dpi = 0; + + _cairo_ft_size_hash (&key); + + size_node = (cairo_ft_size_t*) _cairo_mru_list_find (&state->sizes, (cairo_mru_t*) &key); + + _cairo_ft_face_unref (face_node); + + if (size_node == NULL) + return NULL; + + _cairo_ft_size_ref (size_node); + return size_node->size; + } +} + + +cairo_public void +cairo_freetype_state_unlock (cairo_freetype_state_t *state) +{ + assert (state == _cairo_freetype_state); + + /* destroy any foreign size here */ + while (state->foreign_sizes) { + cairo_freetype_size_ref_t *ref = state->foreign_sizes; + + state->foreign_sizes = ref->next; + FT_Done_Size (ref->size); + free (ref); + } + + /* recycle any used FT_Size, then any remaining FT_Face */ + while (state->sizes.used_nodes != NULL) { + _cairo_mru_unref (state->sizes.used_nodes); + } + + while (state->faces.used_nodes != NULL) + _cairo_mru_unref (state->faces.used_nodes); + + /* release the lock */ + CAIRO_MUTEX_UNLOCK (_cairo_freetype_state_mutex); +} + + +cairo_freetype_font_face_t* +_cairo_font_face_to_freetype (cairo_font_face_t *font_face) +{ + return (font_face->backend == &_cairo_freetype_font_face_backend) + ? (cairo_freetype_font_face_t*) font_face + : NULL; +} + + +#define DOUBLE_TO_16_16(x) ((FT_Fixed)((x)*65536.0)) + + +cairo_public cairo_bool_t +cairo_freetype_compute_transform (cairo_matrix_t *scale, + double *pxscale, + double *pyscale, + cairo_matrix_t *pshape, + FT_Matrix *pftshape) +{ + cairo_matrix_t normalized = *scale; + FT_Matrix ftshape; + + /* The font matrix has x and y "scale" components which we extract and + * use as character scale values. These influence the way freetype + * chooses hints, as well as selecting different bitmaps in + * hand-rendered fonts. We also copy the normalized matrix to + * freetype's transformation. + */ + _cairo_matrix_compute_scale_factors (&normalized, pxscale, pyscale, 1); + + if (*pxscale != 0. && *pyscale != 0.) + cairo_matrix_scale (&normalized, 1.0 / *pxscale, 1.0 / *pyscale); + + ftshape.xx = DOUBLE_TO_16_16 (normalized.xx); + ftshape.xy = DOUBLE_TO_16_16 (normalized.xy); + ftshape.yx = DOUBLE_TO_16_16 (normalized.yx); + ftshape.yy = DOUBLE_TO_16_16 (normalized.yy); + + if (pshape) + *pshape = normalized; + + if (pftshape) + *pftshape = ftshape; + + return (ftshape.xx != 0x10000 || ftshape.yy != 0x10000 || ftshape.xy != 0 || ftshape.yx != 0); +} diff --git a/src/cairo-freetype.h b/src/cairo-freetype.h new file mode 100644 index 0000000..8f11a1b --- /dev/null +++ b/src/cairo-freetype.h @@ -0,0 +1,57 @@ +#ifndef CAIRO_FREETYPE_H +#define CAIRO_FREETYPE_H + +#include + +#ifdef CAIRO_HAS_FT_FONT +#include +#include FT_FREETYPE_H + +CAIRO_BEGIN_DECLS + +cairo_public cairo_font_face_t* +cairo_freetype_font_face_create (const char* pathname, + int face_index, + FT_ULong load_flags); + +/* this function is used to compute the FreeType-specific + * scaling and transform parameters corresponding to a + * given cairo scale matrix + * + * scale :: the input scale matrix + * + * p_xscale :: (output) horizontal scale + * p_yscale :: (output) vertical scale + * p_shape :: (output, optional, can be NULL) shape matrix + * p_ft_shape :: (output, optional, can be NULL) FreeType shape matrix + * + * returns TRUE if the corresponding FreeType shape matrix is *not* the identity + */ +cairo_public cairo_bool_t +cairo_freetype_compute_transform (cairo_matrix_t *scale, + double *p_xscale, + double *p_yscale, + cairo_matrix_t *p_shape, + FT_Matrix *p_ft_shape); + +typedef struct _cairo_freetype_state cairo_freetype_state_t; + +cairo_public cairo_freetype_state_t* +cairo_freetype_state_lock (void); + +cairo_public FT_Face +cairo_freetype_state_get_face (cairo_freetype_state_t *state, + cairo_font_face_t *font_face); + +cairo_public FT_Size +cairo_freetype_state_get_size (cairo_freetype_state_t *state, + cairo_scaled_font_t *scaled_font); + +cairo_public void +cairo_freetype_state_unlock (cairo_freetype_state_t *state); + +CAIRO_END_DECLS + +#endif /* CAIRO_HAS_FT_FONT */ + +#endif /* CAIRO_FREETYPE_H */ -- 1.5.0 From 87e9b633565dbee6481cdc1d111aff189337dc63 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 22 Feb 2007 17:16:44 +0100 Subject: [PATCH] first rewrite of the thread-safe FreeType/FontConfig backend --- src/Makefile.am | 4 +- src/cairo-debug.c | 2 +- src/cairo-fontconfig.c | 325 ++++++++++++++ src/cairo-fontconfig.h | 21 + src/cairo-freetype-font.c | 544 ++++++++++++++++++++++++ src/cairo-freetype-glyph.c | 959 ++++++++++++++++++++++++++++++++++++++++++ src/cairo-freetype-private.h | 148 ++++++- src/cairo-freetype-state.c | 320 ++++++++++----- src/cairo-freetype.h | 50 +-- src/cairo-ft.c | 247 +++++++++++ src/cairo-type1-subset.c | 73 ++-- src/cairoint.h | 7 +- 12 files changed, 2524 insertions(+), 176 deletions(-) create mode 100644 src/cairo-fontconfig.c create mode 100644 src/cairo-fontconfig.h create mode 100644 src/cairo-freetype-font.c create mode 100644 src/cairo-freetype-glyph.c create mode 100644 src/cairo-ft.c diff --git a/src/Makefile.am b/src/Makefile.am index 23573bd..898e135 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -114,7 +114,9 @@ endif if CAIRO_HAS_FT_FONT libcairo_ft_headers = cairo-ft.h cairo-freetype.h cairo-fontconfig.h -libcairo_ft_sources = cairo-ft-font.c cairo-ft-private.h cairo-freetype-private.h cairo-freetype-state.c +libcairo_ft_sources = cairo-ft-private.h \ + cairo-freetype-private.h cairo-freetype-state.c cairo-freetype-glyph.c cairo-freetype-font.c \ + cairo-fontconfig.c cairo-ft.c backend_pkgconfigs += cairo-ft.pc endif diff --git a/src/cairo-debug.c b/src/cairo-debug.c index 7c29932..8c7de5c 100644 --- a/src/cairo-debug.c +++ b/src/cairo-debug.c @@ -66,6 +66,6 @@ cairo_debug_reset_static_data (void) _cairo_font_reset_static_data (); #if CAIRO_HAS_FT_FONT - _cairo_ft_font_reset_static_data (); + _cairo_freetype_font_reset_static_data (); #endif } diff --git a/src/cairo-fontconfig.c b/src/cairo-fontconfig.c new file mode 100644 index 0000000..84510de --- /dev/null +++ b/src/cairo-fontconfig.c @@ -0,0 +1,325 @@ +#include "cairo-fontconfig.h" +#include "cairo-freetype-private.h" + +slim_hidden_proto (cairo_fontconfig_font_face_create); +slim_hidden_proto (cairo_fontconfig_font_options_substitute); + +/** + * cairo_fontconfig_font_face_create: + * @pattern: A fully resolved fontconfig + * pattern. A pattern can be resolved, by, among other things, calling + * FcConfigSubstitute(), FcDefaultSubstitute(), then + * FcFontMatch(). Cairo will call FcPatternReference() on this + * pattern, so you should not further modify the pattern, but you can + * release your reference to the pattern with FcPatternDestroy() if + * you no longer need to access it. + * + * Creates a new font face for the FreeType font backend based on a + * fontconfig pattern. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). The + * #cairo_scaled_font_t returned from cairo_scaled_font_create() is + * also for the FreeType backend and can be used with functions such + * as cairo_freetype_state_get_size(). + * + * Font rendering options are extracted from the pattern and stored into + * the new font face. They will be used when the corresponding options + * passed to cairo_scaled_font_create() are set to their default values. + * + * It is also possible to call cairo_fontconfig_font_options_substitute() + * to substitute the rendering options in a #FcPattern with the ones provided + * by a #cairo_font_options_t. + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t* +cairo_fontconfig_font_face_create (FcPattern *pattern) +{ + FcChar8 * fc_filename; + int fc_index; + + FT_ULong load_flags = 0; + cairo_freetype_face_flags_t extra_flags = 0; + cairo_font_options_t options; + + _cairo_font_options_init_default (&options); + + /* get the (path,index) pair from the pattern */ + if (FcPatternGetString (pattern, FC_FILE, 0, &fc_filename) != FcResultMatch || + FcPatternGetInteger(pattern, FC_INDEX, 0, &fc_index) != FcResultMatch) + return NULL; + + /* we need to find the load_flags, extra_flags and font_options from the pattern */ + { + FcBool vertical_layout, embolden, antialias, bitmaps, hinting, autohint; + + if (FcPatternGetBool (pattern, FC_VERTICAL_LAYOUT, 0, &vertical_layout) == FcResultMatch && vertical_layout) + load_flags |= FT_LOAD_VERTICAL_LAYOUT; + + if (FcPatternGetBool (pattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch && embolden) + extra_flags |= CAIRO_FREETYPE_FLAG_EMBOLDEN; + + if (FcPatternGetBool (pattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch && autohint) + load_flags |= FT_LOAD_FORCE_AUTOHINT; + +#ifndef FC_EMBEDDED_BITMAP +#define FC_EMBEDDED_BITMAP "embeddedbitmap" +#endif + if (FcPatternGetBool (pattern, FC_EMBEDDED_BITMAP, 0, &bitmaps) == FcResultMatch && !bitmaps) + load_flags |= FT_LOAD_NO_BITMAP; + + if (FcPatternGetBool (pattern, FC_ANTIALIAS, 0, &antialias) != FcResultMatch) + antialias = FcTrue; + + if (FcPatternGetBool (pattern, FC_HINTING, 0, &hinting) != FcResultMatch) + hinting = FcTrue; + + if (antialias) + { + cairo_subpixel_order_t order; + int fc_rgba; + + options.antialias = CAIRO_ANTIALIAS_GRAY; + + if (FcPatternGetInteger (pattern, FC_RGBA, 0, &fc_rgba) != FcResultMatch) + fc_rgba = FC_RGBA_UNKNOWN; + + switch (fc_rgba) { + case FC_RGBA_RGB: order = CAIRO_SUBPIXEL_ORDER_RGB; break; + case FC_RGBA_BGR: order = CAIRO_SUBPIXEL_ORDER_BGR; break; + case FC_RGBA_VRGB: order = CAIRO_SUBPIXEL_ORDER_VRGB; break; + case FC_RGBA_VBGR: order = CAIRO_SUBPIXEL_ORDER_VBGR; break; + default: order = CAIRO_SUBPIXEL_ORDER_DEFAULT; + } + + if (order != CAIRO_SUBPIXEL_ORDER_DEFAULT) { + options.antialias = CAIRO_ANTIALIAS_SUBPIXEL; + options.subpixel_order = order; + } + +#ifdef FC_HINT_STYLE + { + int fc_hintstyle; + + if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &fc_hintstyle) != FcResultMatch) + fc_hintstyle = FC_HINT_FULL; + + switch (fc_hintstyle) { + case FC_HINT_NONE: options.hint_style = CAIRO_HINT_STYLE_NONE; break; + case FC_HINT_SLIGHT: options.hint_style = CAIRO_HINT_STYLE_SLIGHT; break; + case FC_HINT_MEDIUM: options.hint_style = CAIRO_HINT_STYLE_MEDIUM; break; + case FC_HINT_FULL: options.hint_style = CAIRO_HINT_STYLE_FULL; break; + } + } +#endif /* FC_HINT_STYLE */ + if (!hinting) + options.hint_style = CAIRO_HINT_STYLE_NONE; + } + else /* !antialias */ + { + options.antialias = CAIRO_ANTIALIAS_NONE; + + /* hint style will be forced later to FT_LOAD_TARGET_MONO by cairo-freetype-glyph.c */ + } + } + + return cairo_freetype_font_face_create ((const char*) fc_filename, fc_index, load_flags, + extra_flags, &options); +} +slim_hidden_def (cairo_fontconfig_font_face_create); + +/** + * cairo_fontconfig_font_options_substitute: + * @options: a #cairo_font_options_t object + * @pattern: an existing #FcPattern + * + * Add options to a #FcPattern based on a #cairo_font_options_t font + * options object. Options that are already in the pattern, are not overridden, + * so you should call this function after calling FcConfigSubstitute() (the + * user's settings should override options based on the surface type), but + * before calling FcDefaultSubstitute(). + **/ +void +cairo_fontconfig_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern) +{ + FcValue v; + + if (options->antialias != CAIRO_ANTIALIAS_DEFAULT) + { + if (FcPatternGet (pattern, FC_ANTIALIAS, 0, &v) == FcResultNoMatch) + { + FcPatternAddBool (pattern, FC_ANTIALIAS, options->antialias != CAIRO_ANTIALIAS_NONE); + if (options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) { + FcPatternDel (pattern, FC_RGBA); + FcPatternAddInteger (pattern, FC_RGBA, FC_RGBA_NONE); + } + } + } + + if (options->antialias != CAIRO_ANTIALIAS_DEFAULT) + { + if (FcPatternGet (pattern, FC_RGBA, 0, &v) == FcResultNoMatch) + { + int rgba; + + if (options->antialias == CAIRO_ANTIALIAS_SUBPIXEL) { + switch (options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + default: + rgba = FC_RGBA_RGB; + break; + case CAIRO_SUBPIXEL_ORDER_BGR: + rgba = FC_RGBA_BGR; + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + rgba = FC_RGBA_VRGB; + break; + case CAIRO_SUBPIXEL_ORDER_VBGR: + rgba = FC_RGBA_VBGR; + break; + } + } else { + rgba = FC_RGBA_NONE; + } + + FcPatternAddInteger (pattern, FC_RGBA, rgba); + } + } + + if (options->hint_style != CAIRO_HINT_STYLE_DEFAULT) + { + if (FcPatternGet (pattern, FC_HINTING, 0, &v) == FcResultNoMatch) + { + FcPatternAddBool (pattern, FC_HINTING, options->hint_style != CAIRO_HINT_STYLE_NONE); + } + +#ifdef FC_HINT_STYLE + if (FcPatternGet (pattern, FC_HINT_STYLE, 0, &v) == FcResultNoMatch) +{ + int hint_style; + + switch (options->hint_style) { + case CAIRO_HINT_STYLE_NONE: + hint_style = FC_HINT_NONE; + break; + case CAIRO_HINT_STYLE_SLIGHT: + hint_style = FC_HINT_SLIGHT; + break; + case CAIRO_HINT_STYLE_MEDIUM: + hint_style = FC_HINT_MEDIUM; + break; + case CAIRO_HINT_STYLE_FULL: + case CAIRO_HINT_STYLE_DEFAULT: + default: + hint_style = FC_HINT_FULL; + break; + } + + FcPatternAddInteger (pattern, FC_HINT_STYLE, hint_style); +} +#endif + } +} +slim_hidden_def (cairo_fontconfig_font_options_substitute); + + +static cairo_status_t +_cairo_fontconfig_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *font_options, + cairo_scaled_font_t **font) +{ + cairo_status_t status; + FcPattern *pattern, *resolved; + cairo_font_face_t *font_face; + FcResult result; + int fcslant, fcweight; + double x_scale, y_scale; + cairo_matrix_t scale; + unsigned char *family = (unsigned char*) toy_face->family; + + pattern = FcPatternCreate (); + if (!pattern) + return CAIRO_STATUS_NO_MEMORY; + + switch (toy_face->weight) + { + case CAIRO_FONT_WEIGHT_BOLD: + fcweight = FC_WEIGHT_BOLD; + break; + case CAIRO_FONT_WEIGHT_NORMAL: + default: + fcweight = FC_WEIGHT_MEDIUM; + break; + } + + switch (toy_face->slant) + { + case CAIRO_FONT_SLANT_ITALIC: + fcslant = FC_SLANT_ITALIC; + break; + case CAIRO_FONT_SLANT_OBLIQUE: + fcslant = FC_SLANT_OBLIQUE; + break; + case CAIRO_FONT_SLANT_NORMAL: + default: + fcslant = FC_SLANT_ROMAN; + break; + } + + status = CAIRO_STATUS_NO_MEMORY; + + if (!FcPatternAddString (pattern, FC_FAMILY, family)) + goto FREE_PATTERN; + if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) + goto FREE_PATTERN; + if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) + goto FREE_PATTERN; + + cairo_matrix_multiply (&scale, font_matrix, ctm); + _cairo_freetype_compute_transform (&scale, &x_scale, &y_scale, NULL, NULL); + + FcPatternAddInteger (pattern, FC_PIXEL_SIZE, y_scale); + + FcConfigSubstitute (NULL, pattern, FcMatchPattern); + cairo_fontconfig_font_options_substitute (font_options, pattern); + FcDefaultSubstitute (pattern); + + resolved = FcFontMatch (NULL, pattern, &result); + if (!resolved) + goto FREE_PATTERN; + + font_face = cairo_fontconfig_font_face_create (resolved); + if (!font_face) + goto FREE_RESOLVED; + + status = _cairo_freetype_font_face_scaled_font_create + (font_face, font_matrix, ctm, font_options, font); + + cairo_font_face_destroy (font_face); + + FREE_RESOLVED: + FcPatternDestroy (resolved); + + FREE_PATTERN: + FcPatternDestroy (pattern); + + return status; +} + + +const cairo_scaled_font_backend_t cairo_fontconfig_scaled_font_backend = { + CAIRO_FONT_TYPE_FT, + _cairo_fontconfig_scaled_font_create_toy, + _cairo_freetype_scaled_font_fini, + _cairo_freetype_scaled_glyph_init, + NULL, /* text_to_glyphs */ + _cairo_freetype_ucs4_to_index, + NULL, /* show_glyphs */ + _cairo_freetype_load_truetype_table, + _cairo_freetype_map_glyphs_to_unicode, +}; + diff --git a/src/cairo-fontconfig.h b/src/cairo-fontconfig.h new file mode 100644 index 0000000..89eb059 --- /dev/null +++ b/src/cairo-fontconfig.h @@ -0,0 +1,21 @@ +#ifndef CAIRO_FONTCONFIG_H +#define CAIRO_FONTCONFIG_H + +#include + +#ifdef CAIRO_HAS_FT_FONT + +#include "cairo-freetype.h" +#include + +cairo_public cairo_font_face_t* +cairo_fontconfig_font_face_create (FcPattern *pattern); + + +cairo_public void +cairo_fontconfig_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern); + +#endif /* CAIRO_HAS_FT_FONT */ + +#endif /* CAIRO_FONTCONFIG_H */ diff --git a/src/cairo-freetype-font.c b/src/cairo-freetype-font.c new file mode 100644 index 0000000..c82964c --- /dev/null +++ b/src/cairo-freetype-font.c @@ -0,0 +1,544 @@ +#include "cairo-freetype-private.h" +#include +#include FT_TRUETYPE_TABLES_H + +#define DOUBLE_FROM_26_6(x) ((x)/64.0) + +static const cairo_font_face_backend_t _cairo_freetype_font_face_backend; + +/* cairo_freetype_font_face_t + * + */ + +static void +_cairo_freetype_font_face_destroy (void *abstract_face) +{ + cairo_freetype_font_face_t *font_face = abstract_face; + + if (font_face == NULL) + return; + + if (font_face->path) { + free ((char*)font_face->path); + font_face->path = NULL; + } +} + + +static const cairo_font_face_backend_t _cairo_freetype_font_face_backend = { + CAIRO_FONT_TYPE_FT, /* XXX: change this to CAIRO_FONT_TYPE_FREETYPE ? */ + _cairo_freetype_font_face_destroy, + _cairo_freetype_font_face_scaled_font_create +}; + + +/** + * cairo_freetype_font_face_create: + * @pathname: A file path to a font file + * @face_index: index of face in font file + * @load_flags: special FreeType load flags, default is 0, some + * of the flags there may be overriden by font options, so only + * a small number of values such as %FT_LOAD_VERTICAL_LAYOUT and + * %FT_LOAD_FORCE_AUTOHINT are recognized. + * @extra_flags: some extra #cairo_freetype_face_flags_t for this + * font face + * @font_options: pointer to Cairo font options to be applied to + * this face, can be NULL. This is the recommended way to specify + * hinting and anti-aliasing settings, instead of playing with + * the #load_flags value + * + * Creates a new font face for the FreeType font backend from a + * (path,index) pair. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). The + * #cairo_scaled_font_t returned from cairo_scaled_font_create() is + * also for the FreeType backend and can be used with functions such + * as cairo_ft_font_lock_face(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t* +cairo_freetype_font_face_create (const char* pathname, + int face_index, + FT_ULong load_flags, + cairo_freetype_face_flags_t extra_flags, + cairo_font_options_t *font_options) +{ + cairo_freetype_font_face_t *font_face; + + if (pathname == NULL) + return NULL; + + font_face = calloc (sizeof(*font_face), 1); + if (font_face == NULL) + return NULL; + + font_face->path = strdup (pathname); + if (font_face->path == NULL) { + free (font_face); + return NULL; + } + + font_face->index = face_index; + font_face->hint_mode = FT_LOAD_TARGET_MODE(load_flags); + font_face->load_flags = load_flags & ~FT_LOAD_TARGET_(font_face->hint_mode); + font_face->extra_flags = extra_flags; + + if (font_options) + font_face->font_options = font_options[0]; + else + _cairo_font_options_init_default (font_options); + + _cairo_font_face_init (&font_face->base, &_cairo_freetype_font_face_backend); + + return (cairo_font_face_t*) font_face; +} +slim_hidden_def (cairo_freetype_font_face_create); + +/** + * cairo_freetype_font_face_create_for_ft_face: + * @face: a FT_Face created by the client. after the call, it will + * be owned by the returned cairo_font_face_t object, and will be + * destroyed with it. + * @load_flags: special FreeType load flags, default is 0, some + * of the flags there may be overriden by font options, so only + * a small number of values such as %FT_LOAD_VERTICAL_LAYOUT and + * %FT_LOAD_FORCE_AUTOHINT are recognized. + * @extra_flags: some extra #cairo_freetype_face_flags_t for this + * font face + * @font_options: pointer to Cairo font options to be applied to + * this face, can be NULL. This is the recommended way to specify + * hinting and anti-aliasing settings, instead of playing with + * the #load_flags value + * + * Creates a new font face for the FreeType font backend from a + * given #FT_Face. This is a variant of #cairo_freetype_font_face_create + * that can be used when the FreeType face is not available directly + * from the filesystem (e.g. an embedded document font fragment) + * + * Note that this face is *not* protected from concurrent usage by + * #cairo_freetype_state_lock, even if its #FT_Face will be returned + * by #cairo_freetype_state_get_face when the result font face is + * passed as a parameter. + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t* +cairo_freetype_font_face_create_for_ft_face (FT_Face face, + FT_ULong load_flags, + cairo_freetype_face_flags_t extra_flags, + cairo_font_options_t *font_options) +{ + cairo_freetype_font_face_t *font_face; + + if (face == NULL) + return NULL; + + font_face = calloc (sizeof(*font_face), 1); + if (font_face == NULL) + return NULL; + + font_face->foreign_face = face; + font_face->hint_mode = FT_LOAD_TARGET_MODE(load_flags); + font_face->load_flags = load_flags & ~FT_LOAD_TARGET_(font_face->hint_mode); + font_face->extra_flags = extra_flags; + + if (font_options) + font_face->font_options = font_options[0]; + else + _cairo_font_options_init_default (font_options); + + _cairo_font_face_init (&font_face->base, &_cairo_freetype_font_face_backend); + + return (cairo_font_face_t*) font_face; +} +slim_hidden_def (cairo_freetype_font_face_create_for_ft_face); + + +cairo_bool_t +_cairo_freetype_font_face_is_vertical (cairo_font_face_t *_font_face) +{ + cairo_freetype_font_face_t* font_face = _cairo_font_face_to_freetype (_font_face); + + return font_face ? (font_face->load_flags & FT_LOAD_VERTICAL_LAYOUT) != 0 : 0; +} + + +cairo_freetype_font_face_t* +_cairo_font_face_to_freetype (cairo_font_face_t *font_face) +{ + if (font_face->backend == &_cairo_freetype_font_face_backend) + return (cairo_freetype_font_face_t*) font_face; + + return NULL; +} + + +/* + * cairo_freetype_scaled_font_t + * + */ + +cairo_freetype_scaled_font_t* +_cairo_scaled_font_to_freetype (cairo_scaled_font_t *scaled_font) +{ + /* IMPORTANT: do note check the scaled_font->backend, since both + * FreeType and FontConfig scaled fonts share the same font face implementation + */ + if (!scaled_font || !_cairo_font_face_to_freetype (scaled_font->font_face)) + return NULL; + + return (cairo_freetype_scaled_font_t*) scaled_font; +} + + + +cairo_bool_t +_cairo_freetype_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font) +{ + if (!scaled_font) + return 0; + + return _cairo_freetype_font_face_is_vertical(scaled_font->font_face); +} + + +cairo_status_t +_cairo_freetype_font_face_scaled_font_create (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **pscaled_font) +{ + cairo_freetype_font_face_t *font_face = abstract_face; + cairo_freetype_scaled_font_t *scaled_font = NULL; + cairo_freetype_state_t *state = NULL; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + FT_Face ft_face; + + scaled_font = malloc (sizeof(*scaled_font)); + if (scaled_font == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto Exit; + } + + _cairo_scaled_font_init (&scaled_font->base, + (cairo_font_face_t*) font_face, + font_matrix, ctm, options, + &cairo_freetype_scaled_font_backend); + + scaled_font->has_shape = _cairo_freetype_compute_transform (&scaled_font->base.scale, + &scaled_font->x_scale, + &scaled_font->y_scale, + &scaled_font->shape, + &scaled_font->ft_shape); + + /* first of all, grab the FreeType state lock */ + state = cairo_freetype_state_lock (); + if (state == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto Exit; + } + + /* compute the scaling parameters needed by cairo_freetype_state_get_size */ + status = cairo_freetype_state_get_face (state, &font_face->base, &ft_face); + if (status) + goto Exit; + + { + scaled_font->ft_char_width = 640; + scaled_font->ft_char_height = 640; + scaled_font->ft_x_dpi = 0; + scaled_font->ft_y_dpi = 0; + scaled_font->ft_pixel_sizes = 0; + + if ( FT_IS_SCALABLE (ft_face) ) + { + scaled_font->ft_char_width = (int) scaled_font->x_scale*64.0; + scaled_font->ft_char_height = (int) scaled_font->y_scale*64.0; + } + else + { + /* for bitmap fonts, try to find the best size */ + double best_dist = DBL_MAX; + int i; + +#ifdef HAVE_FT_BITMAP_SIZE_Y_PPEM + for (i = 0; i < ft_face->num_fixed_sizes; i++) + { + double size = ft_face->available_sizes[i].y_ppem / 64.0; + double dist = fabs (size - scaled_font->y_scale); + + if (dist < best_dist) { + scaled_font->ft_char_width = ft_face->available_sizes[i].x_ppem; + scaled_font->ft_char_height = ft_face->available_sizes[i].y_ppem; + best_dist = dist; + } + } +#else + for (i = 0; i < ft_face->num_fixed_sizes; i++) + { + double size = ft_face->available_sizes[i].y_ppem / 64.; + double dist = fabs (size - scaled_font->y_scale); + + if (dist < best_dist) { + scaled_font->ft_char_width = ft_face->available_sizes[i].width; + scaled_font->ft_char_height = ft_face->available_sizes[i].height; + scaled_font->ft_pixel_sizes = 1; + best_dist = dist; + } + } +#endif + } + } + + /* now compute the scaled font's extents */ + { + FT_Size ft_size; + FT_Size_Metrics *metrics; + cairo_font_extents_t fs_metrics; + + status = cairo_freetype_state_get_size (state, &scaled_font->base, &ft_size); + if (status) + goto Exit; + + metrics = &ft_size->metrics; + + /* + * Get to unscaled metrics so that the upper level can get back to + * user space + */ + if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) + { + double x_factor, y_factor; + + if (scaled_font->x_scale == 0) + x_factor = 0; + else + x_factor = 1 / scaled_font->x_scale; + + if (scaled_font->y_scale == 0) + y_factor = 0; + else + y_factor = 1 / scaled_font->y_scale; + + fs_metrics.ascent = DOUBLE_FROM_26_6(metrics->ascender) * y_factor; + fs_metrics.descent = DOUBLE_FROM_26_6(- metrics->descender) * y_factor; + fs_metrics.height = DOUBLE_FROM_26_6(metrics->height) * y_factor; + + if (!_cairo_freetype_scaled_font_is_vertical (&scaled_font->base)) { + fs_metrics.max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) * x_factor; + fs_metrics.max_y_advance = 0; + } else { + fs_metrics.max_x_advance = 0; + fs_metrics.max_y_advance = DOUBLE_FROM_26_6(metrics->max_advance) * y_factor; + } + } + else + { + double scale = ft_face->units_per_EM; + + fs_metrics.ascent = ft_face->ascender / scale; + fs_metrics.descent = - ft_face->descender / scale; + fs_metrics.height = ft_face->height / scale; + + if (!_cairo_freetype_scaled_font_is_vertical (&scaled_font->base)) { + fs_metrics.max_x_advance = ft_face->max_advance_width / scale; + fs_metrics.max_y_advance = 0; + } else { + fs_metrics.max_x_advance = 0; + fs_metrics.max_y_advance = ft_face->max_advance_height / scale; + } + } + + _cairo_scaled_font_set_metrics (&scaled_font->base, &fs_metrics); + } + +Exit: + if (state != NULL) + cairo_freetype_state_unlock (state); + + if (status && scaled_font) { + cairo_scaled_font_destroy (&scaled_font->base); + free (scaled_font); + scaled_font = NULL; + } + + *pscaled_font = (cairo_scaled_font_t*) scaled_font; + + return status; +} + + + + +void +_cairo_freetype_scaled_font_fini (void *abstract_font) +{ + /* we don't have anything special to do here */ +} + + + + + +static cairo_status_t +_cairo_freetype_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *font_options, + cairo_scaled_font_t **font) +{ + /* we can't create a cairo_freetype_scaled_font_t from a toy face, + * because FreeType itself doesn't provide any way to map a (family,weight,slant) + * tuple into a (path,index) pair. + * + * simply return an error, FILE_NOT_FOUND is probably what sounds the most meaningful error code + */ + return CAIRO_STATUS_FILE_NOT_FOUND; +} + + +cairo_int_status_t +_cairo_freetype_scaled_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_freetype_scaled_font_t *scaled_font = abstract_font; + cairo_freetype_font_face_t *font_face = _cairo_font_face_to_freetype (scaled_font->base.font_face); + cairo_font_options_t options = scaled_font->base.options; + cairo_font_options_t *face_options = &font_face->font_options; + + if (options.antialias == CAIRO_ANTIALIAS_DEFAULT) + options.antialias = face_options->antialias; + + if (options.antialias == CAIRO_ANTIALIAS_SUBPIXEL && + options.subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT) + options.subpixel_order = face_options->subpixel_order; + + if (options.hint_style == CAIRO_HINT_STYLE_DEFAULT) + options.hint_style = face_options->hint_style; + + if (options.hint_metrics == CAIRO_HINT_METRICS_DEFAULT) + options.hint_metrics = face_options->hint_metrics; + + assert (font_face != NULL); + + return _cairo_freetype_scaled_glyph_init_raw (scaled_font, scaled_glyph, info, + font_face->load_flags, + font_face->hint_mode, + font_face->extra_flags, + &options); +} + +unsigned long +_cairo_freetype_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + cairo_freetype_scaled_font_t *scaled_font = abstract_font; + cairo_freetype_state_t *state; + unsigned long result = 0; + + state = cairo_freetype_state_lock(); + if (state != NULL) + { + FT_Face face; + + if ( !cairo_freetype_state_get_face (state, scaled_font->base.font_face, &face) ) + result = FT_Get_Char_Index (face, ucs4); + + cairo_freetype_state_unlock (state); + } + + return result; +} + + + + +cairo_int_status_t +_cairo_freetype_load_truetype_table (void *abstract_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_freetype_scaled_font_t *scaled_font = abstract_font; + cairo_freetype_state_t *state; + FT_Face face; + cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (_cairo_freetype_scaled_font_is_vertical (&scaled_font->base)) + return status; + + state = cairo_freetype_state_lock(); + if (state == NULL) + return CAIRO_STATUS_NO_MEMORY; + + status = cairo_freetype_state_get_face (state, scaled_font->base.font_face, &face); + if (!status) + { +#if HAVE_FT_LOAD_SFNT_TABLE + if (FT_IS_SFNT (face) && + FT_Load_Sfnt_Table (face, tag, offset, buffer, length) == 0) + status = CAIRO_STATUS_SUCCESS; +#endif + } + + cairo_freetype_state_unlock (state); + + return status; +} + + +void +_cairo_freetype_map_glyphs_to_unicode (void *abstract_font, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_freetype_scaled_font_t *scaled_font = abstract_font; + cairo_freetype_state_t *state; + + state = cairo_freetype_state_lock(); + if (state != NULL) + { + FT_Face face; + + if (!cairo_freetype_state_get_face (state, scaled_font->base.font_face, &face)) + { + FT_UInt glyph; + FT_ULong charcode; + int count = font_subset->num_glyphs; + + charcode = FT_Get_First_Char (face, &glyph); + while (glyph != 0 && count > 0) + { + unsigned int nn; + + for (nn = 0; nn < font_subset->num_glyphs; nn++) { + if (font_subset->glyphs[nn] == glyph) { + font_subset->to_unicode[nn] = charcode; + count--; + break; + } + } + charcode = FT_Get_Next_Char (face, charcode, &glyph); + } + } + + cairo_freetype_state_unlock (state); + } +} + + +const cairo_scaled_font_backend_t cairo_freetype_scaled_font_backend = { + CAIRO_FONT_TYPE_FT, + _cairo_freetype_scaled_font_create_toy, + _cairo_freetype_scaled_font_fini, + _cairo_freetype_scaled_glyph_init, + NULL, /* text_to_glyphs */ + _cairo_freetype_ucs4_to_index, + NULL, /* show_glyphs */ + _cairo_freetype_load_truetype_table, + _cairo_freetype_map_glyphs_to_unicode, +}; diff --git a/src/cairo-freetype-glyph.c b/src/cairo-freetype-glyph.c new file mode 100644 index 0000000..3653bcb --- /dev/null +++ b/src/cairo-freetype-glyph.c @@ -0,0 +1,959 @@ +#include "cairo-freetype-private.h" +#include FT_OUTLINE_H +#if HAVE_FT_GLYPHSLOT_EMBOLDEN +#include FT_SYNTHESIS_H +#endif + +#define DOUBLE_FROM_26_6(x) ((x)/64.0) + +/* the following code is related to glyph loading and rendering with FreeType + * due to its length and complexity, it has been separated from the rest of + * Cairo's freetype support sources + */ + +/* Empirically-derived subpixel filtering values thanks to Keith + * Packard and libXft. */ +static const int filters[3][3] = { + /* red */ +#if 0 + { 65538*4/7,65538*2/7,65538*1/7 }, + /* green */ + { 65536*1/4, 65536*2/4, 65537*1/4 }, + /* blue */ + { 65538*1/7,65538*2/7,65538*4/7 }, +#endif + { 65538*9/13,65538*3/13,65538*1/13 }, + /* green */ + { 65538*1/6, 65538*4/6, 65538*1/6 }, + /* blue */ + { 65538*1/13,65538*3/13,65538*9/13 }, +}; + + +/* Fills in val->image with an image surface created from @bitmap + */ +static cairo_status_t +_get_bitmap_surface (FT_Bitmap *bitmap, + cairo_bool_t own_buffer, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) +{ + int width, height, stride; + unsigned char *data; + int format = CAIRO_FORMAT_A8; + cairo_bool_t subpixel = FALSE; + + width = bitmap->width; + height = bitmap->rows; + + switch (bitmap->pixel_mode) { + case FT_PIXEL_MODE_MONO: + stride = (((width + 31) & ~31) >> 3); + if (own_buffer) { + data = bitmap->buffer; + assert (stride == bitmap->pitch); + } else { + data = malloc (stride * height); + if (!data) + return CAIRO_STATUS_NO_MEMORY; + + if (stride == bitmap->pitch) { + memcpy (data, bitmap->buffer, stride * height); + } else { + int i; + unsigned char *source, *dest; + + source = bitmap->buffer; + dest = data; + for (i = height; i; i--) { + memcpy (dest, source, bitmap->pitch); + memset (dest + bitmap->pitch, '\0', stride - bitmap->pitch); + + source += bitmap->pitch; + dest += stride; + } + } + } + +#ifndef WORDS_BIGENDIAN + { + unsigned char *d = data; + int count = stride * height; + + while (count--) { + *d = CAIRO_BITSWAP8 (*d); + d++; + } + } +#endif + format = CAIRO_FORMAT_A1; + break; + + case FT_PIXEL_MODE_LCD: + case FT_PIXEL_MODE_LCD_V: + case FT_PIXEL_MODE_GRAY: + switch (font_options->antialias) { + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_NONE: + default: + stride = bitmap->pitch; + if (own_buffer) { + data = bitmap->buffer; + } else { + data = malloc (stride * height); + if (!data) + return CAIRO_STATUS_NO_MEMORY; + memcpy (data, bitmap->buffer, stride * height); + } + format = CAIRO_FORMAT_A8; + break; + case CAIRO_ANTIALIAS_SUBPIXEL: { + int x, y; + unsigned char *in_line, *out_line, *in; + unsigned int *out; + unsigned int red, green, blue; + int rf, gf, bf; + int s; + int o, os; + unsigned char *data_rgba; + unsigned int width_rgba, stride_rgba; + int vmul = 1; + int hmul = 1; + + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + default: + width /= 3; + hmul = 3; + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + vmul = 3; + height /= 3; + break; + } + /* + * Filter the glyph to soften the color fringes + */ + width_rgba = width; + stride = bitmap->pitch; + stride_rgba = (width_rgba * 4 + 3) & ~3; + data_rgba = calloc (1, stride_rgba * height); + + os = 1; + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_VRGB: + os = stride; + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + default: + rf = 0; + gf = 1; + bf = 2; + break; + case CAIRO_SUBPIXEL_ORDER_VBGR: + os = stride; + case CAIRO_SUBPIXEL_ORDER_BGR: + bf = 0; + gf = 1; + rf = 2; + break; + } + in_line = bitmap->buffer; + out_line = data_rgba; + for (y = 0; y < height; y++) + { + in = in_line; + out = (unsigned int *) out_line; + in_line += stride * vmul; + out_line += stride_rgba; + for (x = 0; x < width * hmul; x += hmul) + { + red = green = blue = 0; + o = 0; + for (s = 0; s < 3; s++) + { + red += filters[rf][s]*in[x+o]; + green += filters[gf][s]*in[x+o]; + blue += filters[bf][s]*in[x+o]; + o += os; + } + red = red / 65536; + green = green / 65536; + blue = blue / 65536; + *out++ = (green << 24) | (red << 16) | (green << 8) | blue; + } + } + + /* Images here are stored in native format. The + * backend must convert to its own format as needed + */ + + if (own_buffer) + free (bitmap->buffer); + data = data_rgba; + stride = stride_rgba; + format = CAIRO_FORMAT_ARGB32; + subpixel = TRUE; + break; + } + } + break; + case FT_PIXEL_MODE_GRAY2: + case FT_PIXEL_MODE_GRAY4: + /* These could be triggered by very rare types of TrueType fonts */ + default: + return CAIRO_STATUS_NO_MEMORY; + } + + *surface = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (data, + format, + width, height, stride); + if ((*surface)->base.status) { + free (data); + return CAIRO_STATUS_NO_MEMORY; + } + + if (subpixel) + pixman_image_set_component_alpha ((*surface)->pixman_image, TRUE); + + _cairo_image_surface_assume_ownership_of_data ((*surface)); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_expand_glyph_outline (FT_Outline *outline, + int hmul, + int vmul) +{ + FT_Vector *vec = outline->points; + FT_Vector *vec_end = vec + outline->n_points; + + if (hmul > 1) + for (; vec < vec_end; vec++) + vec->x *= 3; + else if (vmul > 1) + for (; vec < vec_end; vec++) + vec->y *= 3; +} + +static void +_restore_glyph_outline (FT_Outline *outline, + int hmul, + int vmul) +{ + FT_Vector *vec = outline->points; + FT_Vector *vec_end = vec + outline->n_points; + + if (hmul > 1) + for (; vec < vec_end; vec++) + vec->x /= 3; + else if (vmul > 1) + for (; vec < vec_end; vec++) + vec->y /= 3; +} + + +/* Converts an outline FT_GlyphSlot into an image + * + * This could go through _render_glyph_bitmap as well, letting + * FreeType convert the outline to a bitmap, but doing it ourselves + * has two minor advantages: first, we save a copy of the bitmap + * buffer: we can directly use the buffer that FreeType renders + * into. + * + * Second, it may help when we add support for subpixel + * rendering: the Xft code does it this way. (Keith thinks that + * it may also be possible to get the subpixel rendering with + * FT_Render_Glyph: something worth looking into in more detail + * when we add subpixel support. If so, we may want to eliminate + * this version of the code path entirely. + */ +static cairo_status_t +_render_glyph_outline (FT_Face face, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) +{ + FT_GlyphSlot glyphslot = face->glyph; + FT_Outline *outline = &glyphslot->outline; + FT_Bitmap bitmap; + FT_BBox cbox; + int hmul = 1; + int vmul = 1; + unsigned int width, height, stride; + cairo_bool_t subpixel = FALSE; + cairo_status_t status; + + FT_Outline_Get_CBox (outline, &cbox); + + cbox.xMin &= ~63; + cbox.yMin &= ~63; + cbox.xMax = (cbox.xMax + 63) & ~63; + cbox.yMax = (cbox.yMax + 63) & ~63; + + width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); + height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); + stride = (width * hmul + 3) & ~3; + + if (width * height == 0) { + cairo_format_t format; + switch (font_options->antialias) { + case CAIRO_ANTIALIAS_NONE: + format = CAIRO_FORMAT_A1; + break; + case CAIRO_ANTIALIAS_SUBPIXEL: + format= CAIRO_FORMAT_ARGB32; + break; + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + default: + format = CAIRO_FORMAT_A8; + break; + } + + (*surface) = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); + if ((*surface)->base.status) + return CAIRO_STATUS_NO_MEMORY; + } else { + + switch (font_options->antialias) { + case CAIRO_ANTIALIAS_NONE: + bitmap.pixel_mode = FT_PIXEL_MODE_MONO; + bitmap.num_grays = 1; + stride = ((width + 31) & -32) >> 3; + break; + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap.num_grays = 256; + stride = (width + 3) & -4; + break; + case CAIRO_ANTIALIAS_SUBPIXEL: + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + default: + hmul = 3; + subpixel = TRUE; + _expand_glyph_outline (outline, 3, 1); + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + vmul = 3; + subpixel = TRUE; + _expand_glyph_outline (outline, 1, 3); + break; + } + + bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap.num_grays = 256; + stride = (width * hmul + 3) & -4; + } + + bitmap.pitch = stride; + bitmap.width = width * hmul; + bitmap.rows = height * vmul; + bitmap.buffer = calloc (1, stride * bitmap.rows); + + if (bitmap.buffer == NULL) { + _restore_glyph_outline (outline, hmul, vmul); + return CAIRO_STATUS_NO_MEMORY; + } + + FT_Outline_Translate (outline, -cbox.xMin*hmul, -cbox.yMin*vmul); + + if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { + free (bitmap.buffer); + status = CAIRO_STATUS_NO_MEMORY; + } + else { + status = _get_bitmap_surface (&bitmap, TRUE, font_options, surface); + } + + /* restore outline to its initial state */ + FT_Outline_Translate (outline, cbox.xMin*hmul, cbox.yMin*vmul); + + if (status) + return status; + } + + /* + * Note: the font's coordinate system is upside down from ours, so the + * Y coordinate of the control box needs to be negated. + */ + cairo_surface_set_device_offset (&(*surface)->base, + floor ((double) cbox.xMin / 64.0), + floor (-(double) cbox.yMax / 64.0)); + + return CAIRO_STATUS_SUCCESS; +} + +/* Converts a bitmap (or other) FT_GlyphSlot into an image */ +static cairo_status_t +_render_glyph_bitmap (FT_Face face, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) +{ + FT_GlyphSlot glyphslot = face->glyph; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + FT_Error error; + + /* According to the FreeType docs, glyphslot->format could be + * something other than FT_GLYPH_FORMAT_OUTLINE or + * FT_GLYPH_FORMAT_BITMAP. Calling FT_Render_Glyph gives FreeType + * the opportunity to convert such to + * bitmap. FT_GLYPH_FORMAT_COMPOSITE will not be encountered since + * we avoid the FT_LOAD_NO_RECURSE flag. + */ + error = FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL); + if (error) + return CAIRO_STATUS_NO_MEMORY; + + status = _get_bitmap_surface (&glyphslot->bitmap, FALSE, font_options, surface); + if (status) + return status; + + /* + * Note: the font's coordinate system is upside down from ours, so the + * Y coordinate of the control box needs to be negated. + */ + cairo_surface_set_device_offset (&(*surface)->base, + glyphslot->bitmap_left, + -glyphslot->bitmap_top); + + return status; +} + +static cairo_status_t +_transform_glyph_bitmap (cairo_matrix_t * shape, + cairo_image_surface_t ** surface) +{ + cairo_matrix_t original_to_transformed; + cairo_matrix_t transformed_to_original; + cairo_image_surface_t *old_image; + cairo_surface_t *image; + double x[4], y[4]; + double origin_x, origin_y; + int origin_width, origin_height; + int i; + int x_min, y_min, x_max, y_max; + int width, height; + cairo_status_t status; + cairo_surface_pattern_t pattern; + + /* We want to compute a transform that takes the origin + * (device_x_offset, device_y_offset) to 0,0, then applies + * the "shape" portion of the font transform + */ + original_to_transformed = *shape; + + cairo_surface_get_device_offset (&(*surface)->base, &origin_x, &origin_y); + origin_width = cairo_image_surface_get_width (&(*surface)->base); + origin_height = cairo_image_surface_get_height (&(*surface)->base); + + cairo_matrix_translate (&original_to_transformed, + origin_x, origin_y); + + /* Find the bounding box of the original bitmap under that + * transform + */ + x[0] = 0; y[0] = 0; + x[1] = origin_width; y[1] = 0; + x[2] = origin_width; y[2] = origin_height; + x[3] = 0; y[3] = origin_height; + + for (i = 0; i < 4; i++) + cairo_matrix_transform_point (&original_to_transformed, + &x[i], &y[i]); + + x_min = floor (x[0]); y_min = floor (y[0]); + x_max = ceil (x[0]); y_max = ceil (y[0]); + + for (i = 1; i < 4; i++) { + if (x[i] < x_min) + x_min = floor (x[i]); + if (x[i] > x_max) + x_max = ceil (x[i]); + if (y[i] < y_min) + y_min = floor (y[i]); + if (y[i] > y_max) + y_max = ceil (y[i]); + } + + /* Adjust the transform so that the bounding box starts at 0,0 ... + * this gives our final transform from original bitmap to transformed + * bitmap. + */ + original_to_transformed.x0 -= x_min; + original_to_transformed.y0 -= y_min; + + /* Create the transformed bitmap + */ + width = x_max - x_min; + height = y_max - y_min; + + transformed_to_original = original_to_transformed; + status = cairo_matrix_invert (&transformed_to_original); + if (status) + return status; + + /* We need to pad out the width to 32-bit intervals for cairo-xlib-surface.c */ + width = (width + 3) & ~3; + image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); + if (image->status) + return CAIRO_STATUS_NO_MEMORY; + + /* Initialize it to empty + */ + _cairo_surface_fill_rectangle (image, CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + 0, 0, + width, height); + + /* Draw the original bitmap transformed into the new bitmap + */ + _cairo_pattern_init_for_surface (&pattern, &(*surface)->base); + cairo_pattern_set_matrix (&pattern.base, &transformed_to_original); + + _cairo_surface_composite (CAIRO_OPERATOR_OVER, + &pattern.base, NULL, image, + 0, 0, 0, 0, 0, 0, + width, + height); + + _cairo_pattern_fini (&pattern.base); + + /* Now update the cache entry for the new bitmap, recomputing + * the origin based on the final transform. + */ + origin_x = - origin_x; + origin_y = - origin_y; + cairo_matrix_transform_point (&original_to_transformed, + &origin_x, &origin_y); + + old_image = (*surface); + (*surface) = (cairo_image_surface_t *)image; + cairo_surface_destroy (&old_image->base); + + cairo_surface_set_device_offset (&(*surface)->base, + - _cairo_lround (origin_x), + - _cairo_lround (origin_y)); + return status; +} + + +static int +_move_to (FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + cairo_fixed_t x, y; + + x = _cairo_fixed_from_26_6 (to->x); + y = _cairo_fixed_from_26_6 (to->y); + + _cairo_path_fixed_close_path (path); + _cairo_path_fixed_move_to (path, x, y); + + return 0; +} + +static int +_line_to (FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + cairo_fixed_t x, y; + + x = _cairo_fixed_from_26_6 (to->x); + y = _cairo_fixed_from_26_6 (to->y); + + _cairo_path_fixed_line_to (path, x, y); + + return 0; +} + +static int +_conic_to (FT_Vector *control, FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + + cairo_fixed_t x0, y0; + cairo_fixed_t x1, y1; + cairo_fixed_t x2, y2; + cairo_fixed_t x3, y3; + cairo_point_t conic; + + _cairo_path_fixed_get_current_point (path, &x0, &y0); + + conic.x = _cairo_fixed_from_26_6 (control->x); + conic.y = _cairo_fixed_from_26_6 (control->y); + + x3 = _cairo_fixed_from_26_6 (to->x); + y3 = _cairo_fixed_from_26_6 (to->y); + + x1 = x0 + 2.0/3.0 * (conic.x - x0); + y1 = y0 + 2.0/3.0 * (conic.y - y0); + + x2 = x3 + 2.0/3.0 * (conic.x - x3); + y2 = y3 + 2.0/3.0 * (conic.y - y3); + + _cairo_path_fixed_curve_to (path, + x1, y1, + x2, y2, + x3, y3); + + return 0; +} + +static int +_cubic_to (FT_Vector *control1, FT_Vector *control2, + FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + cairo_fixed_t x0, y0; + cairo_fixed_t x1, y1; + cairo_fixed_t x2, y2; + + x0 = _cairo_fixed_from_26_6 (control1->x); + y0 = _cairo_fixed_from_26_6 (control1->y); + + x1 = _cairo_fixed_from_26_6 (control2->x); + y1 = _cairo_fixed_from_26_6 (control2->y); + + x2 = _cairo_fixed_from_26_6 (to->x); + y2 = _cairo_fixed_from_26_6 (to->y); + + _cairo_path_fixed_curve_to (path, + x0, y0, + x1, y1, + x2, y2); + + return 0; +} + +static cairo_status_t +_decompose_glyph_outline (FT_Face face, + cairo_font_options_t *options, + cairo_path_fixed_t **pathp) +{ + static const FT_Outline_Funcs outline_funcs = { + (FT_Outline_MoveToFunc)_move_to, + (FT_Outline_LineToFunc)_line_to, + (FT_Outline_ConicToFunc)_conic_to, + (FT_Outline_CubicToFunc)_cubic_to, + 0, /* shift */ + 0, /* delta */ + }; + static const FT_Matrix invert_y = { + 0x10000, 0, + 0, -0x10000 + }; + + FT_GlyphSlot glyph; + cairo_path_fixed_t *path; + + path = _cairo_path_fixed_create (); + if (!path) + return CAIRO_STATUS_NO_MEMORY; + + glyph = face->glyph; + + /* Font glyphs have an inverted Y axis compared to cairo. */ + FT_Outline_Transform (&glyph->outline, &invert_y); + FT_Outline_Decompose (&glyph->outline, &outline_funcs, path); + FT_Outline_Transform (&glyph->outline, &invert_y); + + _cairo_path_fixed_close_path (path); + + *pathp = path; + + return CAIRO_STATUS_SUCCESS; +} + +/* + * Translate glyph to match its metrics. + */ +static void +_vertical_layout_bearing_fix (cairo_freetype_scaled_font_t *scaled_font, + FT_GlyphSlot glyph) +{ + FT_Vector vector; + + vector.x = glyph->metrics.vertBearingX - glyph->metrics.horiBearingX; + vector.y = -glyph->metrics.vertBearingY - glyph->metrics.horiBearingY; + + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) + { + FT_Vector_Transform (&vector, &scaled_font->ft_shape); + FT_Outline_Translate(&glyph->outline, vector.x, vector.y); + } + else if (glyph->format == FT_GLYPH_FORMAT_BITMAP) + { + glyph->bitmap_left += vector.x / 64; + glyph->bitmap_top += vector.y / 64; + } +} + + +cairo_int_status_t +_cairo_freetype_scaled_glyph_init_raw (cairo_freetype_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info, + FT_ULong load_flags, + FT_Render_Mode hint_mode, + cairo_freetype_face_flags_t extra_flags, + cairo_font_options_t *font_options) +{ + cairo_text_extents_t fs_metrics; + cairo_freetype_state_t *ft_state = NULL; + FT_GlyphSlot glyph; + FT_Face face; + FT_Size size; + FT_Error error; + FT_Glyph_Metrics *metrics; + cairo_bool_t vertical_layout = FALSE; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + ft_state = cairo_freetype_state_lock (); + if (ft_state == NULL) + return CAIRO_STATUS_NO_MEMORY; + + status = cairo_freetype_state_get_size (ft_state, &scaled_font->base, &size); + if (status) + goto FAIL; + + face = size->face; + + /* Ignore global advance unconditionally */ + load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; + + if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 && + (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0) + load_flags |= FT_LOAD_NO_BITMAP; + + /* merge hinting modes */ + if (font_options->antialias == CAIRO_ANTIALIAS_NONE) + { + hint_mode = FT_RENDER_MODE_MONO; + if (font_options->hint_style == CAIRO_HINT_STYLE_NONE) + load_flags |= FT_LOAD_NO_HINTING; + } + else + { + switch (font_options->hint_style) + { + case CAIRO_HINT_STYLE_NONE: + load_flags |= FT_LOAD_NO_HINTING; + break; + + case CAIRO_HINT_STYLE_SLIGHT: + hint_mode = FT_RENDER_MODE_LIGHT; + load_flags |= FT_LOAD_FORCE_AUTOHINT; + break; + + case CAIRO_HINT_STYLE_MEDIUM: + /* medium hinting always uses the auto-hinter */ + /* use full hinting to enabled native hints, e.g. */ + /* with a bytecode-interpreter-enabled FreeType build */ + hint_mode = FT_RENDER_MODE_NORMAL; + load_flags |= FT_LOAD_FORCE_AUTOHINT; + break; + + case CAIRO_HINT_STYLE_FULL: + hint_mode = FT_RENDER_MODE_NORMAL; + load_flags &= ~FT_LOAD_FORCE_AUTOHINT; + if (font_options->antialias == CAIRO_ANTIALIAS_SUBPIXEL) { + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + hint_mode = FT_RENDER_MODE_LCD; + break; + + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + hint_mode = FT_RENDER_MODE_LCD_V; + break; + } + } + break; + + case CAIRO_HINT_STYLE_DEFAULT: + /* use hint_mode from original font_face */ + ; + } + } + + load_flags |= FT_LOAD_TARGET_(hint_mode); + + /* + * Don't pass FT_LOAD_VERTICAL_LAYOUT to FT_Load_Glyph here as + * suggested by freetype people. + */ + if (load_flags & FT_LOAD_VERTICAL_LAYOUT) { + load_flags &= ~FT_LOAD_VERTICAL_LAYOUT; + vertical_layout = TRUE; + } + + error = FT_Load_Glyph (size->face, + _cairo_scaled_glyph_index(scaled_glyph), + load_flags); + + if (error) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + glyph = face->glyph; + +#if HAVE_FT_GLYPHSLOT_EMBOLDEN + /* + * embolden glyphs if requested + */ + if (extra_flags & CAIRO_FREETYPE_FLAG_EMBOLDEN) + FT_GlyphSlot_Embolden (glyph); +#endif + + if (vertical_layout) + _vertical_layout_bearing_fix (scaled_font, glyph); + + if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) + { + double x_factor, y_factor; + + /* + * Compute font-space metrics + */ + metrics = &glyph->metrics; + + if (scaled_font->x_scale == 0) + x_factor = 0; + else + x_factor = 1 / scaled_font->x_scale; + + if (scaled_font->y_scale == 0) + y_factor = 0; + else + y_factor = 1 / scaled_font->y_scale; + + /* + * Note: Y coordinates of the horizontal bearing need to be negated. + * + * Scale metrics back to glyph space from the scaled glyph space returned + * by FreeType + * + * If we want hinted metrics but aren't asking for hinted glyphs from + * FreeType, then we need to do the metric hinting ourselves. + */ + + if ((font_options->hint_metrics != CAIRO_HINT_METRICS_OFF) && + (load_flags & FT_LOAD_NO_HINTING)) + { + FT_Pos x1, x2; + FT_Pos y1, y2; + FT_Pos advance; + + if (!vertical_layout) { + x1 = (metrics->horiBearingX) & ~63; + x2 = (metrics->horiBearingX + metrics->width + 63) & ~63; + y1 = (-metrics->horiBearingY) & ~63; + y2 = (-metrics->horiBearingY + metrics->height + 63) & ~63; + + advance = ((metrics->horiAdvance + 32) & ~63); + + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; + + fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; + + fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor; + fs_metrics.y_advance = 0; + } else { + x1 = (metrics->vertBearingX) & ~63; + x2 = (metrics->vertBearingX + metrics->width + 63) & ~63; + y1 = (metrics->vertBearingY) & ~63; + y2 = (metrics->vertBearingY + metrics->height + 63) & ~63; + + advance = ((metrics->vertAdvance + 32) & ~63); + + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; + + fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; + + fs_metrics.x_advance = 0; + fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor; + } + } else { + fs_metrics.width = DOUBLE_FROM_26_6 (metrics->width) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor; + + if (!vertical_layout) { + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor; + + fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor; + fs_metrics.y_advance = 0 * y_factor; + } else { + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor; + + fs_metrics.x_advance = 0 * x_factor; + fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor; + } + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &fs_metrics); + } + + if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { + cairo_image_surface_t *surface; + + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) + { + status = _render_glyph_outline (face, font_options, &surface); + } else + { + status = _render_glyph_bitmap (face, font_options, &surface); + if (status == CAIRO_STATUS_SUCCESS && scaled_font->has_shape) + status = _transform_glyph_bitmap (&scaled_font->shape, &surface); + } + if (status) + goto FAIL; + + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + surface); + } + + if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { + cairo_path_fixed_t *path; + + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) + status = _decompose_glyph_outline (face, font_options, &path); + else + status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (status) + goto FAIL; + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + } + FAIL: + if (ft_state) + cairo_freetype_state_unlock (ft_state); + + return status; +} diff --git a/src/cairo-freetype-private.h b/src/cairo-freetype-private.h index 9279472..666a58a 100644 --- a/src/cairo-freetype-private.h +++ b/src/cairo-freetype-private.h @@ -6,31 +6,161 @@ CAIRO_BEGIN_DECLS +/* define CAIRO_FREETYPE_UNSAFE_SUPPORT if you want to compile support + * for thread-unsafe operations cairo_ft_scaled_font_lock_face() and + * cairo_ft_scaled_font_unlock_face() into your build + */ +#define CAIRO_FREETYPE_UNSAFE_SUPPORT + /* the FreeType font face holds either a (path,index) pair, or a foreign FT_Face * object that was created by the client, and thus belongs to a different FT_Library * instance. * * note that the foreign FT_Face is owned by the font_face object and will disappear * with it. + * + * the 'hint_mode' field is stored separatly from the 'load_flags' for easier + * debugging and replacement when merging FontConfig and scaled font options */ -typedef struct _cairo_freetype_font_face_t { - cairo_font_face_t base; - const char* path; - int index; - FT_Face foreign_face; - FT_ULong load_flags; +typedef struct _cairo_freetype_font_face { + cairo_font_face_t base; + const char* path; + int index; + FT_Face foreign_face; + FT_ULong load_flags; + FT_Render_Mode hint_mode; + cairo_freetype_face_flags_t extra_flags; + cairo_font_options_t font_options; } cairo_freetype_font_face_t; -cairo_private const cairo_font_face_backend_t _cairo_freetype_font_face_backend; + +/* unlike the previous implementation, we define a FreeType-specific cairo_scaled_font_t + * sub-class. it simply contains scaling and shape parameters computed from the initial + * font => device scale matrix + */ +typedef struct _cairo_freetype_scaled_font_t { + cairo_scaled_font_t base; + double x_scale; /* Extracted X scale factor */ + double y_scale; /* Extracted Y scale factor */ + cairo_bool_t has_shape; /* true if the current scale has a non-scale component*/ + cairo_matrix_t shape; + FT_Matrix ft_shape; + int ft_char_width; + int ft_char_height; + cairo_bool_t ft_pixel_sizes; + int ft_x_dpi; + int ft_y_dpi; + +#ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT + /* despite its name, the following is a *copy* of the + * parent's font_face's FT_Face that was created in a + * custom FT_Library instance. this allows any thread + * to use the unsafe_ft_face concurrently from Cairo + */ + FT_Face unsafe_ft_face; +#endif + +} cairo_freetype_scaled_font_t; + +#define CAIRO_FREETYPE_SCALED_FONT__LOAD_FLAGS(f) \ + ((cairo_freetype_font_face_t*)((f)->base.font_face))->load_flags + +#define CAIRO_FREETYPE_SCALED_FONT__HINT_MODE(f) \ + ((cairo_freetype_font_face_t*)(((f)->base.font_face))->hint_mode + +#define CAIRO_FREETYPE_SCALED_FONT__EXTRA_FLAGS(f) \ + ((cairo_freetype_font_face_t*)((f)->base.font_face))->extra_flags + /* dynamic cast to a freetype font face, or NULL */ -cairo_freetype_font_face_t* +cairo_private cairo_freetype_font_face_t* _cairo_font_face_to_freetype (cairo_font_face_t *font_face); +cairo_private cairo_status_t +_cairo_freetype_font_face_scaled_font_create (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **scaled_font); + +/* dynamic cast to a freetype scaled font, or NULL */ +cairo_private cairo_freetype_scaled_font_t* +_cairo_scaled_font_to_freetype (cairo_scaled_font_t *scaled_font); + + +/* this function is used to compute the FreeType-specific + * scaling and transform parameters corresponding to a + * given cairo scale matrix + * + * scale :: the input scale matrix + * + * p_xscale :: (output) horizontal scale + * p_yscale :: (output) vertical scale + * p_shape :: (output, optional, can be NULL) shape matrix + * p_ft_shape :: (output, optional, can be NULL) FreeType shape matrix + * + * returns TRUE if the corresponding FreeType shape matrix is *not* the identity + */ +cairo_private cairo_bool_t +_cairo_freetype_compute_transform (cairo_matrix_t *scale, + double *p_xscale, + double *p_yscale, + cairo_matrix_t *p_shape, + FT_Matrix *p_ft_shape); + +cairo_private cairo_bool_t +_cairo_freetype_font_face_is_vertical (cairo_font_face_t *font_face); cairo_private cairo_bool_t -_cairo_font_face_is_freetype (cairo_font_face_t *font_face); +_cairo_freetype_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_freetype_state_reset (void); + + +/* the following are exposed since they're also used by cairo_fontconfig_scaled_font_t */ + +cairo_private void +_cairo_freetype_scaled_font_fini (void *abstract_font); + + +cairo_private unsigned long +_cairo_freetype_ucs4_to_index (void *abstract_font, + uint32_t ucs4); + +cairo_private cairo_int_status_t +_cairo_freetype_load_truetype_table (void *abstract_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length); + +cairo_private void +_cairo_freetype_map_glyphs_to_unicode (void *abstract_font, + cairo_scaled_font_subset_t *font_subset); + +cairo_private cairo_int_status_t +_cairo_freetype_scaled_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info); + +cairo_private cairo_int_status_t +_cairo_freetype_scaled_glyph_init_raw (cairo_freetype_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info, + FT_ULong load_flags, + FT_Render_Mode hint_mode, + cairo_freetype_face_flags_t extra_flags, + cairo_font_options_t *font_options); + +/* */ +slim_hidden_proto (cairo_freetype_font_face_create); +slim_hidden_proto (cairo_freetype_font_face_create_for_ft_face); +slim_hidden_proto (cairo_freetype_state_lock); +slim_hidden_proto (cairo_freetype_state_get_face); +slim_hidden_proto (cairo_freetype_state_get_size); +slim_hidden_proto (cairo_freetype_state_unlock); CAIRO_END_DECLS diff --git a/src/cairo-freetype-state.c b/src/cairo-freetype-state.c index 40b8c1f..1f87693 100644 --- a/src/cairo-freetype-state.c +++ b/src/cairo-freetype-state.c @@ -1,8 +1,33 @@ +/* the following source is used to maintain Cairo's FreeType "state" + * + * the state contains a single FT_Library handle, as well as two MRU-ordered + * list of FT_Face and FT_Size objects. We try to limit the size of each list, + * because these objects can have a very large size in the heap, depending on + * the font file's design and format. + * + * the state is also protected by a mutex, since FreeType is not a multi-threaded library + */ #include "cairo-freetype-private.h" #include #include #include FT_SIZES_H +static cairo_status_t +_cairo_status_from_ft_error (FT_Error error) +{ + cairo_status_t status; + + switch (error) { + case FT_Err_Ok: status = CAIRO_STATUS_SUCCESS; break; + case FT_Err_Out_Of_Memory: status = CAIRO_STATUS_NO_MEMORY; break; + case FT_Err_Cannot_Open_Stream: status = CAIRO_STATUS_FILE_NOT_FOUND; break; + default: + status = CAIRO_STATUS_NO_MEMORY; /* XXX: for not better choice */ + } + return status; +} + + /* technical explanation of what's going on here: * * because FT_Face and FT_Size objects can have very large sizes in the heap, @@ -99,6 +124,7 @@ struct _cairo_mru_list { void *param1; void *param2; cairo_mru_backend_t backend; + cairo_status_t status; }; @@ -129,8 +155,12 @@ _cairo_mru_prepend (cairo_mru_t **phead, node->next = node; node->prev = node; } else { - node->next = first; - node->prev = first->prev; + cairo_mru_t* last = first->prev; + + last->next = node; + first->prev = node; + node->next = first; + node->prev = last; } *phead = node; } @@ -181,11 +211,14 @@ _cairo_mru_list_new_node (cairo_mru_list_t *list, node->ref_count = 0; /* important !! */ node->list = list; - if ( backend->node_init (node, init) != 0 ) { + list->status = backend->node_init (node, init); + if (list->status != 0) { backend->node_done (node); free (node); node = NULL; } + } else { + list->status = CAIRO_STATUS_NO_MEMORY; } return node; } @@ -207,6 +240,7 @@ _cairo_mru_list_init (cairo_mru_list_t *list, list->param1 = param1; list->param2 = param2; list->backend = backend[0]; + list->status = CAIRO_STATUS_SUCCESS; } @@ -222,6 +256,8 @@ _cairo_mru_list_find (cairo_mru_list_t *list, { cairo_mru_t *node, *first; + list->status = CAIRO_STATUS_SUCCESS; + /* first look in the used_nodes list */ first = list->used_nodes; if (first != NULL) { @@ -670,6 +706,22 @@ static cairo_freetype_state_t *_cairo_freetype_state; static void +_cairo_freetype_state_destroy (cairo_freetype_state_t *state) +{ + assert (state->foreign_sizes == NULL); + assert (state->faces.used_nodes == NULL); + assert (state->sizes.used_nodes == NULL); + + _cairo_mru_list_done (&state->sizes); + _cairo_mru_list_done (&state->faces); + + FT_Done_FreeType (state->library); + state->library = NULL; + + free (state); +} + +static void _cairo_freetype_state_create (void) { cairo_freetype_state_t *state = calloc( sizeof(*state), 1 ); @@ -690,7 +742,29 @@ _cairo_freetype_state_create (void) _cairo_freetype_state = state; } - +/** + * cairo_freetype_state_lock: + * + * Locks the FreeType library instance used by Cairo and all child + * FT_Face and FT_Size objects, until a call to cairo_freetype_state_unlock(). + * + * Since each FreeType library instance is not thread-safe, access to the + * corresponding FT_Face and FT_Size objects must be synchronized to avoid + * race-conditions, floating errors and crashes. + * + * Cairo's FreeType state consists in a single FT_Library instance, with + * one or more FT_Face and FT_Size child objects, all protected by a single + * mutex. A client that needs to use a FT_Face and/or FT_Size object should + * call this function to acquire the lock. + * + * cairo_freetype_state_get_face() and cairo_freetype_state_get_size() can + * then be used to retrieve relevant FT_Face and FT_Size handles; these can + * be used freely by a single thread until cairo_freetype_state_unlock() + * is called. + * + * Return value: a freetype state handle, which must be used in calls to other + * freetype state-related functions + **/ cairo_freetype_state_t* cairo_freetype_state_lock (void) { @@ -705,7 +779,7 @@ cairo_freetype_state_lock (void) } return _cairo_freetype_state; } - +slim_hidden_def (cairo_freetype_state_lock); static cairo_ft_face_t* _cairo_freetype_state_get_ft_face (cairo_freetype_state_t *state, @@ -727,53 +801,103 @@ _cairo_freetype_state_get_ft_face (cairo_freetype_state_t *state, } -FT_Face +/** + * cairo_freetype_state_get_face: + * @state: Cairo FreeType state handle + * @font_face: source FreeType font face pointer + * @p_face: address of returned FT_Face + * + * Retrieve the FT_Face handle corresponding to a given FreeType font face. + * The FreeType 'state' handle must be acquired previously with cairo_freetype_state_lock(). + * As a convenience, the FT_Face's transform is set to the identity when this function + * returns. + * + * If the font face was created through cairo_freetype_font_face_create_for_ft_face(), + * the original FT_Face handle is returned. The client must protect multiple threads from + * accessing the object though. + * + * Note that the returned FT_Face handle shall *not* be used after a call to + * cairo_freetype_state_unlock() ! + * + * Return value: a status value + **/ +cairo_status_t cairo_freetype_state_get_face (cairo_freetype_state_t *state, - cairo_font_face_t *_font_face) + cairo_font_face_t *_font_face, + FT_Face *p_face) { cairo_freetype_font_face_t *font_face = _cairo_font_face_to_freetype (_font_face); cairo_ft_face_t *face_node; if (font_face == NULL) - return NULL; + return CAIRO_STATUS_NULL_POINTER; if (font_face->foreign_face) - return font_face->foreign_face; + { + *p_face = font_face->foreign_face; + } else { face_node = _cairo_freetype_state_get_ft_face (state, font_face); if (face_node == NULL) - return NULL; + return state->faces.status; - _cairo_ft_face_ref (face_node); - return face_node->face; + *p_face = face_node->face; } -} + FT_Set_Transform (*p_face, NULL, NULL); -FT_Size + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_freetype_state_get_face); + +/** + * cairo_freetype_state_get_size: + * @state: Cairo FreeType state handle + * @scaled_font: source FreeType scaled font pointer + * @p_size: address of returned FT_Size + * + * Retrieve the FT_Size handle corresponding to a given FreeType scaled font. + * As a convenience, the return FT_Size is activated when the function returns, and + * its parent FT_Face's transform and set to the scaled font's shape transform + * + * The FreeType 'state' handle must be acquired previously with cairo_freetype_state_lock(). + * + * Note that the returned FT_Size handle automatically becomes invalid after a call + * to cairo_freetype_state_unlock() ! + * + * Return value: a status value + **/ +cairo_status_t cairo_freetype_state_get_size (cairo_freetype_state_t *state, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + FT_Size *p_size) { - FT_Size size; - cairo_freetype_font_face_t *font_face; + FT_Size size; + cairo_freetype_font_face_t *font_face; + cairo_freetype_scaled_font_t *scaled; if (scaled_font == NULL) - return NULL; + return CAIRO_STATUS_NULL_POINTER; font_face = _cairo_font_face_to_freetype (scaled_font->font_face); if (font_face == NULL) - return NULL; + return CAIRO_STATUS_NO_MEMORY; /* XXX: find better error code ? */ - if (font_face->foreign_face) { + scaled = (cairo_freetype_scaled_font_t*) scaled_font; + + if (font_face->foreign_face) + { cairo_freetype_size_ref_t *foreign_size; + FT_Error error; foreign_size = malloc (sizeof(*foreign_size)); if (foreign_size == NULL) - return NULL; + return CAIRO_STATUS_NO_MEMORY; - if ( FT_New_Size (font_face->foreign_face, &size) != 0 ) { + error = FT_New_Size (font_face->foreign_face, &size); + if (error) { free (foreign_size); - return NULL; + return _cairo_status_from_ft_error (error); } foreign_size->size = size; @@ -781,88 +905,75 @@ cairo_freetype_state_get_size (cairo_freetype_state_t *state, state->foreign_sizes = foreign_size; FT_Activate_Size (size); - return size; - } else { + *p_size = size; + } + else + { cairo_ft_face_t *face_node; cairo_ft_size_t *size_node; cairo_ft_size_t key; face_node = _cairo_freetype_state_get_ft_face (state, font_face); if (face_node == NULL) - return NULL; - - key.face = face_node; - - /* XXX: we recompute everything on this one, but these numbers should */ - /* really be stored in the cairo_ft_scaled_font instead !!! */ - { - double x_scale, y_scale; - cairo_bool_t have_shape; - - have_shape = cairo_freetype_compute_transform ( &scaled_font->scale, &x_scale, &y_scale, NULL, NULL ); - - if ( FT_IS_SCALABLE (face_node->face) ) - { - key.width = (int) x_scale*64.0; - key.height = (int) y_scale*64.0; - key.pixel_sizes = 0; - } else - { - /* for bitmap fonts, try to find the best size */ - FT_Face face = face_node->face; - double best_dist = DBL_MAX; - int best_width = 10, best_height = 10; - int i; - - for (i = 0; i < face->num_fixed_sizes; i++) - { -#ifdef HAVE_FT_BITMAP_SIZE_Y_PPEM - double size = face->available_sizes[i].y_ppem / 64; -#else - double size = face->available_sizes[i].height; -#endif - double dist = fabs (size - y_scale); - - if (dist < best_dist) { -#ifdef HAVE_FT_BITMAP_SIZE_Y_PPEM - best_width = face->available_sizes[i].x_ppem; - best_height = face->available_sizes[i].y_ppem; -#else - best_width = face->available_sizes[i].width; - best_height = face->available_sizes[i].height; -#endif - best_dist = dist; - } - } - - key.width = best_width; - key.height = best_height; -#ifdef HAVE_FT_BITMAP_SIZE_Y_PPEM - key.pixel_sizes = 0; -#else - key.pixel_sizes = 1; -#endif - } + return state->faces.status; + + key.face = face_node; + key.width = scaled->ft_char_width; + key.height = scaled->ft_char_height; + key.pixel_sizes = scaled->ft_pixel_sizes; + if (!key.pixel_sizes) { + key.x_dpi = scaled->ft_x_dpi; + key.y_dpi = scaled->ft_y_dpi; } - key.x_dpi = 0; - key.y_dpi = 0; _cairo_ft_size_hash (&key); size_node = (cairo_ft_size_t*) _cairo_mru_list_find (&state->sizes, (cairo_mru_t*) &key); + if (size_node == NULL) { + _cairo_ft_face_unref (face_node); + return state->sizes.status; + } + + _cairo_ft_size_ref (size_node); _cairo_ft_face_unref (face_node); - if (size_node == NULL) - return NULL; + *p_size = size_node->size; + } - _cairo_ft_size_ref (size_node); - return size_node->size; + FT_Activate_Size (*p_size); + + /* set the shape transform from the scaled font face */ + { + FT_Face face = (*p_size)->face; + cairo_freetype_scaled_font_t *scaled = _cairo_scaled_font_to_freetype (scaled_font); + + if (scaled && scaled->has_shape) + FT_Set_Transform (face, &scaled->ft_shape, NULL); + else + FT_Set_Transform (face, NULL, NULL); } + return CAIRO_STATUS_SUCCESS; } +slim_hidden_def (cairo_freetype_state_get_size); - -cairo_public void +/** + * cairo_freetype_state_unlock: + * @state: Cairo FreeType state handle + * + * Release the Cairo FreeType state lock. This also recycle/releases all. + * + * As a convenience, the return FT_Size is activated when the function returns, and + * its parent FT_Face's transform and set to the scaled font's shape transform. + * + * The FreeType 'state' handle must be acquired previously with cairo_freetype_state_lock(). + * + * Note that the returned FT_Size handle automatically becomes invalid after a call + * to cairo_freetype_state_unlock() !. + * + * Return value: a status value. + **/ +void cairo_freetype_state_unlock (cairo_freetype_state_t *state) { assert (state == _cairo_freetype_state); @@ -887,26 +998,17 @@ cairo_freetype_state_unlock (cairo_freetype_state_t *state) /* release the lock */ CAIRO_MUTEX_UNLOCK (_cairo_freetype_state_mutex); } - - -cairo_freetype_font_face_t* -_cairo_font_face_to_freetype (cairo_font_face_t *font_face) -{ - return (font_face->backend == &_cairo_freetype_font_face_backend) - ? (cairo_freetype_font_face_t*) font_face - : NULL; -} - +slim_hidden_def (cairo_freetype_state_unlock); #define DOUBLE_TO_16_16(x) ((FT_Fixed)((x)*65536.0)) -cairo_public cairo_bool_t -cairo_freetype_compute_transform (cairo_matrix_t *scale, - double *pxscale, - double *pyscale, - cairo_matrix_t *pshape, - FT_Matrix *pftshape) +cairo_bool_t +_cairo_freetype_compute_transform (cairo_matrix_t *scale, + double *pxscale, + double *pyscale, + cairo_matrix_t *pshape, + FT_Matrix *pftshape) { cairo_matrix_t normalized = *scale; FT_Matrix ftshape; @@ -935,3 +1037,15 @@ cairo_freetype_compute_transform (cairo_matrix_t *scale, return (ftshape.xx != 0x10000 || ftshape.yy != 0x10000 || ftshape.xy != 0 || ftshape.yx != 0); } + + +void +_cairo_freetype_font_reset_static_data (void) +{ + CAIRO_MUTEX_LOCK(_cairo_freetype_state_mutex); + if (_cairo_freetype_state) { + _cairo_freetype_state_destroy (_cairo_freetype_state); + _cairo_freetype_state = NULL; + } + CAIRO_MUTEX_UNLOCK(_cairo_freetype_state_mutex); +} diff --git a/src/cairo-freetype.h b/src/cairo-freetype.h index 8f11a1b..b13d79a 100644 --- a/src/cairo-freetype.h +++ b/src/cairo-freetype.h @@ -9,43 +9,39 @@ CAIRO_BEGIN_DECLS + +typedef enum _cairo_freetype_face_flags { + CAIRO_FREETYPE_FLAG_EMBOLDEN = (1 << 0) +} cairo_freetype_face_flags_t; + + +cairo_public cairo_font_face_t* +cairo_freetype_font_face_create (const char* pathname, + int face_index, + FT_ULong load_flags, + cairo_freetype_face_flags_t extra_flags, + cairo_font_options_t *font_options); + cairo_public cairo_font_face_t* -cairo_freetype_font_face_create (const char* pathname, - int face_index, - FT_ULong load_flags); - -/* this function is used to compute the FreeType-specific - * scaling and transform parameters corresponding to a - * given cairo scale matrix - * - * scale :: the input scale matrix - * - * p_xscale :: (output) horizontal scale - * p_yscale :: (output) vertical scale - * p_shape :: (output, optional, can be NULL) shape matrix - * p_ft_shape :: (output, optional, can be NULL) FreeType shape matrix - * - * returns TRUE if the corresponding FreeType shape matrix is *not* the identity - */ -cairo_public cairo_bool_t -cairo_freetype_compute_transform (cairo_matrix_t *scale, - double *p_xscale, - double *p_yscale, - cairo_matrix_t *p_shape, - FT_Matrix *p_ft_shape); +cairo_freetype_font_face_create_for_ft_face (FT_Face face, + FT_ULong load_flags, + cairo_freetype_face_flags_t extra_flags, + cairo_font_options_t *font_options); typedef struct _cairo_freetype_state cairo_freetype_state_t; cairo_public cairo_freetype_state_t* cairo_freetype_state_lock (void); -cairo_public FT_Face +cairo_public cairo_status_t cairo_freetype_state_get_face (cairo_freetype_state_t *state, - cairo_font_face_t *font_face); + cairo_font_face_t *font_face, + FT_Face *p_face); -cairo_public FT_Size +cairo_public cairo_status_t cairo_freetype_state_get_size (cairo_freetype_state_t *state, - cairo_scaled_font_t *scaled_font); + cairo_scaled_font_t *scaled_font, + FT_Size *p_size); cairo_public void cairo_freetype_state_unlock (cairo_freetype_state_t *state); diff --git a/src/cairo-ft.c b/src/cairo-ft.c new file mode 100644 index 0000000..053cec8 --- /dev/null +++ b/src/cairo-ft.c @@ -0,0 +1,247 @@ +#include "cairo-ft.h" +#include "cairo-freetype.h" +#include "cairo-fontconfig.h" +#include "cairo-freetype-private.h" + +slim_hidden_proto (cairo_fontconfig_font_face_create); +slim_hidden_proto (cairo_fontconfig_font_options_substitute); + + +cairo_public void +cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font); + +/** + * cairo_ft_scaled_font_lock_face: + * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an + * object can be created by calling cairo_scaled_font_create() on a + * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), + * cairo_ft_font_face_create_for_face()). + * + * cairo_ft_font_lock_face() gets the #FT_Face object from a FreeType + * backend font and scales it appropriately for the font. You must + * release the face with cairo_ft_font_unlock_face() + * when you are done using it. Since the #FT_Face object can be + * shared between multiple #cairo_scaled_font_t objects, you must not + * lock any other font objects until you unlock this one. A count is + * kept of the number of times cairo_ft_font_lock_face() is + * called. cairo_ft_font_unlock_face() must be called the same number + * of times. + * + * THIS FUNCTION IS DEPRECATED AND UNSAFE WHEN USED IN MULTI-THREADED + * PROGRAMS. CONSIDER USING cairo_freetype_state_lock() and related + * functions to access FT_Face and/or FT_Size objects instead !! + * + * Return value: The #FT_Face object for @font, scaled appropriately, + * or %NULL if @scaled_font is in an error state (see + * cairo_scaled_font_status()) or there is insufficient memory. + **/ +FT_Face +cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) +{ +#ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT + /* we do something absolutely hideous, but that guarantees that + * clients will never have problems: we create a new FT_Library + * instance, and *copy* the content of the original FT_Face into + * it... + */ + cairo_freetype_scaled_font_t *scaled_font; + cairo_freetype_font_face_t *font_face; + FT_Library library; + FT_Face unsafe_face = NULL; + + scaled_font = _cairo_scaled_font_to_freetype(abstract_font); + if (scaled_font == NULL) + return NULL; + + CAIRO_MUTEX_LOCK (scaled_font->base.mutex); + if (scaled_font->unsafe_ft_face) { + unsafe_face = scaled_font->unsafe_ft_face; + goto EXIT; + } + + font_face = (cairo_freetype_font_face_t*) scaled_font->base.font_face; + + if ( FT_Init_FreeType (&library) != 0 ) + goto EXIT; + + if (font_face->foreign_face == NULL) + { + /* easy copy: grab the (path,index) pair and re-open the font */ + /* if FT_New_Face fails, unsafe_face will be set to NULL */ + FT_New_Face (library, font_face->path, font_face->index, &unsafe_face); + } + else + { + /* this is a *lot* more tricky, though pretty much rare */ + /* as far as we're concerned, this unsafe API is only used */ + /* by Pango on Unix, where FreeType streams are always memory-mapped */ + /* by default. */ + /* */ + /* Note that in theory, you'll likely crash your application if you */ + /* destroy the parent font face before the locked FT_Face, but this */ + /* should be seen as just punishment for using this brain-dead */ + /* API anyway */ + /* */ + FT_Face org_face = font_face->foreign_face; + FT_Stream stream = org_face->stream; + + if (stream->base == NULL || stream->size == 0) + goto EXIT; + + /* if this call fails, unsafe_face is set to NULL */ + FT_New_Memory_Face (library, stream->base, stream->size, + org_face->face_index, &unsafe_face); + } + +EXIT: + scaled_font->unsafe_ft_face = unsafe_face; + + /* scale the face */ + if (unsafe_face != NULL) { + if (scaled_font->ft_pixel_sizes) + FT_Set_Pixel_Sizes (unsafe_face, + scaled_font->ft_char_width, + scaled_font->ft_char_height ); + else + FT_Set_Char_Size (unsafe_face, + scaled_font->ft_char_width, + scaled_font->ft_char_height, + scaled_font->ft_x_dpi, + scaled_font->ft_y_dpi); + + if (scaled_font->has_shape) + FT_Set_Transform (unsafe_face, &scaled_font->ft_shape, NULL); + } + + CAIRO_MUTEX_UNLOCK (scaled_font->base.mutex); + return unsafe_face; +#else + return NULL; +#endif +} + +/** + * cairo_ft_scaled_font_unlock_face: + * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an + * object can be created by calling cairo_scaled_font_create() on a + * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), + * cairo_ft_font_face_create_for_ft_face()). + * + * Releases a face obtained with cairo_ft_scaled_font_lock_face(). + **/ +void +cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) +{ +#ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT + cairo_freetype_scaled_font_t *scaled_font; + FT_Face face; + + scaled_font = _cairo_scaled_font_to_freetype (abstract_font); + if (scaled_font == NULL) + return; + + CAIRO_MUTEX_LOCK (scaled_font->base.mutex); + face = scaled_font->unsafe_ft_face; + if (face != NULL) { + /* the following automatically destroyes the FT_Face */ + FT_Done_FreeType (face->glyph->library); + scaled_font->unsafe_ft_face; + } + CAIRO_MUTEX_UNLOCK (scaled_font->base.mutex); +#endif +} + + +/** + * cairo_ft_font_face_create_for_ft_face: + * @face: A FreeType face object, already opened. This must + * be kept around until the face's ref_count drops to + * zero and it is freed. Since the face may be referenced + * internally to Cairo, the best way to determine when it + * is safe to free the face is to pass a + * #cairo_destroy_func_t to cairo_font_face_set_user_data() + * @load_flags: flags to pass to FT_Load_Glyph when loading + * glyphs from the font. These flags are OR'ed together with + * the flags derived from the #cairo_font_options_t passed + * to cairo_scaled_font_create(), so only a few values such + * as %FT_LOAD_VERTICAL_LAYOUT, and %FT_LOAD_FORCE_AUTOHINT + * are useful. You should not pass any of the flags affecting + * the load target, such as %FT_LOAD_TARGET_LIGHT. + * + * THIS FUNCTION IS DEPRECATED, USE cairo_freetype_font_face_create_for_ft_face() + * INSTEAD TO DO THE SAME THING !!. + * + * Creates a new font face for the FreeType font backend from a + * pre-opened FreeType face. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). The + * #cairo_scaled_font_t returned from cairo_scaled_font_create() is + * also for the FreeType backend and can be used with functions such + * as cairo_ft_font_lock_face(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t * +cairo_ft_font_face_create_for_ft_face (FT_Face face, + int load_flags) +{ + return cairo_freetype_font_face_create_for_ft_face (face, load_flags, 0, NULL); +} + + +/** + * cairo_ft_font_options_substitute: + * @options: a #cairo_font_options_t object + * @pattern: an existing #FcPattern + * + * THIS FUNCTION IS DEPRECATED, USE cairo_fontconfig_font_options_substitute() + * INSTEAD TO DO THE SAME THING. + * + * Add options to a #FcPattern based on a #cairo_font_options_t font + * options object. Options that are already in the pattern, are not overridden, + * so you should call this function after calling FcConfigSubstitute() (the + * user's settings should override options based on the surface type), but + * before calling FcDefaultSubstitute(). + **/ +void +cairo_ft_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern) +{ + cairo_fontconfig_font_options_substitute (options, pattern); +} + + +/** + * cairo_ft_font_face_create_for_pattern: + * @pattern: A fully resolved fontconfig + * pattern. A pattern can be resolved, by, among other things, calling + * FcConfigSubstitute(), FcDefaultSubstitute(), then + * FcFontMatch(). Cairo will call FcPatternReference() on this + * pattern, so you should not further modify the pattern, but you can + * release your reference to the pattern with FcPatternDestroy() if + * you no longer need to access it. + * + * THIS FUNCTION IS DEPRECATED, USE cairo_fontconfig_font_face_create() + * TO DO THE SAME THING + * + * Creates a new font face for the FreeType font backend based on a + * fontconfig pattern. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). The + * #cairo_scaled_font_t returned from cairo_scaled_font_create() is + * also for the FreeType backend and can be used with functions such + * as cairo_ft_font_lock_face(). + * + * Font rendering options are represented both here and when you + * call cairo_scaled_font_create(). Font options that have a representation + * in a #FcPattern must be passed in here; to modify #FcPattern + * appropriately to reflect the options in a #cairo_font_options_t, call + * cairo_ft_font_options_substitute(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t * +cairo_ft_font_face_create_for_pattern (FcPattern *pattern) +{ + return cairo_fontconfig_font_face_create (pattern); +} diff --git a/src/cairo-type1-subset.c b/src/cairo-type1-subset.c index 73b0b90..cc93394 100644 --- a/src/cairo-type1-subset.c +++ b/src/cairo-type1-subset.c @@ -38,7 +38,7 @@ #include "cairo-output-stream-private.h" /* XXX: Eventually, we need to handle other font backends */ -#include "cairo-ft-private.h" +#include "cairo-freetype-private.h" #include #include FT_FREETYPE_H @@ -50,7 +50,7 @@ typedef struct _cairo_type1_font_subset { cairo_scaled_font_subset_t *scaled_font_subset; struct { - cairo_unscaled_font_t *unscaled_font; + cairo_font_face_t *font_face; unsigned int font_id; char *base_font; unsigned int num_glyphs; @@ -102,20 +102,23 @@ typedef struct _cairo_type1_font_subset { static cairo_status_t -_cairo_type1_font_subset_create (cairo_unscaled_font_t *unscaled_font, +_cairo_type1_font_subset_create (cairo_font_face_t *font_face, cairo_type1_font_subset_t **subset_return, cairo_bool_t hex_encode) { - cairo_ft_unscaled_font_t *ft_unscaled_font; + cairo_freetype_state_t *ft_state; cairo_status_t status; FT_Face face; PS_FontInfoRec font_info; cairo_type1_font_subset_t *font; int i, j; - ft_unscaled_font = (cairo_ft_unscaled_font_t *) unscaled_font; + ft_state = cairo_freetype_state_lock(); + if (ft_state == NULL) + return CAIRO_STATUS_NO_MEMORY; - face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font); + status = cairo_freetype_state_get_face (ft_state, font_face, &face); + if (status) goto fail1; if (FT_Get_PS_Font_Info(face, &font_info) != 0) { status = CAIRO_INT_STATUS_UNSUPPORTED; @@ -128,7 +131,7 @@ _cairo_type1_font_subset_create (cairo_unscaled_font_t *unscaled_font, goto fail1; } - font->base.unscaled_font = _cairo_unscaled_font_reference (unscaled_font); + font->base.font_face = cairo_font_face_reference (font_face); font->base.num_glyphs = face->num_glyphs; font->base.x_min = face->bbox.xMin; font->base.y_min = face->bbox.yMin; @@ -160,9 +163,9 @@ _cairo_type1_font_subset_create (cairo_unscaled_font_t *unscaled_font, for (i = 0; i < face->num_glyphs; i++) font->glyphs[i].subset_index = -1; - _cairo_array_init (&font->contents, sizeof (char)); + cairo_freetype_state_unlock (ft_state); - _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); + _cairo_array_init (&font->contents, sizeof (char)); *subset_return = font; @@ -170,12 +173,12 @@ _cairo_type1_font_subset_create (cairo_unscaled_font_t *unscaled_font, fail3: free (font->base.base_font); + fail2: - _cairo_unscaled_font_destroy (unscaled_font); + cairo_font_face_destroy (font_face); free (font); fail1: - _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); - + cairo_freetype_state_unlock (ft_state); return status; } @@ -1039,29 +1042,36 @@ cairo_type1_font_subset_generate (void *abstract_font, { cairo_type1_font_subset_t *font = abstract_font; - cairo_ft_unscaled_font_t *ft_unscaled_font; - unsigned long ret; + cairo_freetype_state_t *state; + cairo_font_face_t *font_face; + FT_Face face; + unsigned long ret; - ft_unscaled_font = (cairo_ft_unscaled_font_t *) font->base.unscaled_font; - font->face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font); + font_face = font->base.font_face; + + state = cairo_freetype_state_lock(); + if (state == NULL) goto EXIT; + + font->status = cairo_freetype_state_get_face (state, font_face, &face); + if (font->status) goto fail; /* If anything fails below, it's out of memory. */ font->status = CAIRO_STATUS_NO_MEMORY; - font->type1_length = font->face->stream->size; - font->type1_data = malloc (font->type1_length); + font->type1_length = face->stream->size; + font->type1_data = malloc (font->type1_length); if (font->type1_data == NULL) - goto fail; + goto fail; - if (font->face->stream->read) { - ret = font->face->stream->read (font->face->stream, 0, - (unsigned char *) font->type1_data, - font->type1_length); + if (face->stream->read) { + ret = face->stream->read (face->stream, 0, + (unsigned char *) font->type1_data, + font->type1_length); if (ret != font->type1_length) goto fail; } else { memcpy (font->type1_data, - font->face->stream->base, font->type1_length); + face->stream->base, font->type1_length); } if (_cairo_array_grow_by (&font->contents, 4096) != CAIRO_STATUS_SUCCESS) @@ -1077,8 +1087,9 @@ cairo_type1_font_subset_generate (void *abstract_font, font->base.data = _cairo_array_index (&font->contents, 0); fail: - _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); + cairo_freetype_state_unlock (state); + EXIT: return font->status; } @@ -1099,7 +1110,7 @@ cairo_type1_font_subset_destroy (void *abstract_font) free (font->glyphs[i].name); } - _cairo_unscaled_font_destroy (font->base.unscaled_font); + cairo_font_face_destroy (font->base.font_face); free (font->base.base_font); free (font->glyphs); @@ -1117,18 +1128,16 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, cairo_status_t status; unsigned long parent_glyph, length; unsigned int i; - cairo_unscaled_font_t *unscaled_font; + cairo_scaled_font_t *scaled_font = scaled_font_subset->scaled_font; /* XXX: Need to fix this to work with a general cairo_unscaled_font_t. */ - if (!_cairo_scaled_font_is_ft (scaled_font_subset->scaled_font)) + if (!_cairo_scaled_font_to_freetype (scaled_font)) return CAIRO_INT_STATUS_UNSUPPORTED; - if (_cairo_ft_scaled_font_is_vertical (scaled_font_subset->scaled_font)) + if (_cairo_freetype_scaled_font_is_vertical (scaled_font)) return CAIRO_INT_STATUS_UNSUPPORTED; - unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font_subset->scaled_font); - - status = _cairo_type1_font_subset_create (unscaled_font, &font, hex_encode); + status = _cairo_type1_font_subset_create (scaled_font->font_face, &font, hex_encode); if (status) return status; diff --git a/src/cairoint.h b/src/cairoint.h index 3a2d845..db0b484 100755 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -623,7 +623,7 @@ cairo_private void _cairo_font_reset_static_data (void); cairo_private void -_cairo_ft_font_reset_static_data (void); +_cairo_freetype_font_reset_static_data (void); cairo_private void _cairo_xlib_surface_reset_static_data (void); @@ -748,7 +748,8 @@ struct _cairo_font_face_backend { /* concrete font backends */ #if CAIRO_HAS_FT_FONT -extern const cairo_private struct _cairo_scaled_font_backend cairo_ft_scaled_font_backend; +extern const cairo_private struct _cairo_scaled_font_backend cairo_freetype_scaled_font_backend; +extern const cairo_private struct _cairo_scaled_font_backend cairo_fontconfig_scaled_font_backend; #endif @@ -1213,7 +1214,7 @@ typedef struct _cairo_traps { #elif CAIRO_HAS_FT_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT -#define CAIRO_SCALED_FONT_BACKEND_DEFAULT &cairo_ft_scaled_font_backend +#define CAIRO_SCALED_FONT_BACKEND_DEFAULT &cairo_fontconfig_scaled_font_backend #endif -- 1.5.0 From 8d13aa0466dc8caa1ace7e62be1bc39b7d0c16e9 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 23 Feb 2007 10:34:09 +0100 Subject: [PATCH] fix various scaling bugs --- src/cairo-freetype-font.c | 10 +++++++--- src/cairo-freetype-glyph.c | 2 +- src/cairo-freetype-state.c | 8 ++++---- src/cairo-ft-font.c | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/cairo-freetype-font.c b/src/cairo-freetype-font.c index c82964c..74ac464 100644 --- a/src/cairo-freetype-font.c +++ b/src/cairo-freetype-font.c @@ -147,7 +147,7 @@ cairo_freetype_font_face_create_for_ft_face (FT_Face face, if (font_options) font_face->font_options = font_options[0]; else - _cairo_font_options_init_default (font_options); + _cairo_font_options_init_default (&font_face->font_options); _cairo_font_face_init (&font_face->base, &_cairo_freetype_font_face_backend); @@ -228,6 +228,10 @@ _cairo_freetype_font_face_scaled_font_create (void *abstra font_matrix, ctm, options, &cairo_freetype_scaled_font_backend); +#ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT + scaled_font->unsafe_ft_face = NULL; +#endif + scaled_font->has_shape = _cairo_freetype_compute_transform (&scaled_font->base.scale, &scaled_font->x_scale, &scaled_font->y_scale, @@ -255,8 +259,8 @@ _cairo_freetype_font_face_scaled_font_create (void *abstra if ( FT_IS_SCALABLE (ft_face) ) { - scaled_font->ft_char_width = (int) scaled_font->x_scale*64.0; - scaled_font->ft_char_height = (int) scaled_font->y_scale*64.0; + scaled_font->ft_char_width = (int)( scaled_font->x_scale*64.0 ); + scaled_font->ft_char_height = (int)( scaled_font->y_scale*64.0 ); } else { diff --git a/src/cairo-freetype-glyph.c b/src/cairo-freetype-glyph.c index 3653bcb..b143866 100644 --- a/src/cairo-freetype-glyph.c +++ b/src/cairo-freetype-glyph.c @@ -759,7 +759,7 @@ _cairo_freetype_scaled_glyph_init_raw (cairo_freetype_scaled_font_t *scaled_fon case CAIRO_HINT_STYLE_MEDIUM: /* medium hinting always uses the auto-hinter */ - /* use full hinting to enabled native hints, e.g. */ + /* use full hinting to enable native hints, e.g. */ /* with a bytecode-interpreter-enabled FreeType build */ hint_mode = FT_RENDER_MODE_NORMAL; load_flags |= FT_LOAD_FORCE_AUTOHINT; diff --git a/src/cairo-freetype-state.c b/src/cairo-freetype-state.c index 1f87693..6b09404 100644 --- a/src/cairo-freetype-state.c +++ b/src/cairo-freetype-state.c @@ -1024,10 +1024,10 @@ _cairo_freetype_compute_transform (cairo_matrix_t *scale, if (*pxscale != 0. && *pyscale != 0.) cairo_matrix_scale (&normalized, 1.0 / *pxscale, 1.0 / *pyscale); - ftshape.xx = DOUBLE_TO_16_16 (normalized.xx); - ftshape.xy = DOUBLE_TO_16_16 (normalized.xy); - ftshape.yx = DOUBLE_TO_16_16 (normalized.yx); - ftshape.yy = DOUBLE_TO_16_16 (normalized.yy); + ftshape.xx = DOUBLE_TO_16_16 (normalized.xx); + ftshape.xy = - DOUBLE_TO_16_16 (normalized.xy); + ftshape.yx = - DOUBLE_TO_16_16 (normalized.yx); + ftshape.yy = DOUBLE_TO_16_16 (normalized.yy); if (pshape) *pshape = normalized; diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 7650f47..97f7424 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -1349,7 +1349,7 @@ _cairo_ft_options_merge (cairo_ft_options_t *options, /* clear load target mode */ load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(other->load_flags))); - + if (load_flags & FT_LOAD_NO_HINTING) other->base.hint_style = CAIRO_HINT_STYLE_NONE; -- 1.5.0 From 3ba0e0987ee345bc40c26677e29bc8f012963c43 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 23 Feb 2007 10:45:39 +0100 Subject: [PATCH] fix unsafe FT_Face support --- src/cairo-freetype-font.c | 1 + src/cairo-freetype-private.h | 1 + src/cairo-ft.c | 8 ++++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/cairo-freetype-font.c b/src/cairo-freetype-font.c index 74ac464..529137f 100644 --- a/src/cairo-freetype-font.c +++ b/src/cairo-freetype-font.c @@ -230,6 +230,7 @@ _cairo_freetype_font_face_scaled_font_create (void *abstra #ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT scaled_font->unsafe_ft_face = NULL; + scaled_font->unsafe_count = 0; #endif scaled_font->has_shape = _cairo_freetype_compute_transform (&scaled_font->base.scale, diff --git a/src/cairo-freetype-private.h b/src/cairo-freetype-private.h index 666a58a..2616903 100644 --- a/src/cairo-freetype-private.h +++ b/src/cairo-freetype-private.h @@ -59,6 +59,7 @@ typedef struct _cairo_freetype_scaled_font_t { * to use the unsafe_ft_face concurrently from Cairo */ FT_Face unsafe_ft_face; + int unsafe_count; #endif } cairo_freetype_scaled_font_t; diff --git a/src/cairo-ft.c b/src/cairo-ft.c index 053cec8..4908be8 100644 --- a/src/cairo-ft.c +++ b/src/cairo-ft.c @@ -96,8 +96,11 @@ cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) EXIT: scaled_font->unsafe_ft_face = unsafe_face; + /* scale the face */ if (unsafe_face != NULL) { + scaled_font->unsafe_count++; + if (scaled_font->ft_pixel_sizes) FT_Set_Pixel_Sizes (unsafe_face, scaled_font->ft_char_width, @@ -142,10 +145,11 @@ cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) CAIRO_MUTEX_LOCK (scaled_font->base.mutex); face = scaled_font->unsafe_ft_face; - if (face != NULL) { + if (face != NULL && --scaled_font->unsafe_count <= 0) { /* the following automatically destroyes the FT_Face */ FT_Done_FreeType (face->glyph->library); - scaled_font->unsafe_ft_face; + scaled_font->unsafe_ft_face = NULL; + scaled_font->unsafe_count = 0; /* just in case */ } CAIRO_MUTEX_UNLOCK (scaled_font->base.mutex); #endif -- 1.5.0 From 23ad42ff65565cb4b839edb2ec01318219f4f555 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 23 Feb 2007 13:49:30 +0100 Subject: [PATCH] fix freetype state cache bugs --- src/cairo-freetype-private.h | 8 ++++++-- src/cairo-freetype-state.c | 12 +++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/cairo-freetype-private.h b/src/cairo-freetype-private.h index 2616903..5f64f45 100644 --- a/src/cairo-freetype-private.h +++ b/src/cairo-freetype-private.h @@ -54,9 +54,13 @@ typedef struct _cairo_freetype_scaled_font_t { #ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT /* despite its name, the following is a *copy* of the - * parent's font_face's FT_Face that was created in a - * custom FT_Library instance. this allows any thread + * parent's font_face's FT_Face that was created in its + * own FT_Library instance. this allows any thread * to use the unsafe_ft_face concurrently from Cairo + * + * on the other hand, this is bad because creating the + * FT_Face takes time, and it also uses a lot more heap + * memory... */ FT_Face unsafe_ft_face; int unsafe_count; diff --git a/src/cairo-freetype-state.c b/src/cairo-freetype-state.c index 6b09404..22fa2d1 100644 --- a/src/cairo-freetype-state.c +++ b/src/cairo-freetype-state.c @@ -177,12 +177,16 @@ _cairo_mru_raise (cairo_mru_t **phead, if (node != first) { cairo_mru_t *next = node->next; cairo_mru_t *prev = node->prev; + cairo_mru_t *last; prev->next = next; next->prev = prev; - node->next = first; - node->prev = first->prev; + last = first->prev; + last->next = node; + first->prev = node; + node->next = first; + node->prev = first->prev; *phead = node; } @@ -340,12 +344,14 @@ _cairo_mru_list_remove_select (cairo_mru_list_t *list, node = first; do { + cairo_mru_t *node_next = node->next; + if ( (*equal_func) (node, (cairo_mru_t*)equal_key) ) { _cairo_mru_remove (&list->free_nodes, node); list->num_nodes--; _cairo_mru_list_free_node (list, node); } - node = node->next; + node = node_next; } while (node != first); } -- 1.5.0 From 18aeb8b7b7f91716ecdc6e15fbe8c4ffbcde7bc8 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 23 Feb 2007 16:51:31 +0100 Subject: [PATCH] change the implementation of unsafe operations by using a second, unprotected, cairo_freetype_state_t object this gets rid of over-zealous memory usage in large applications like FireFox. --- src/cairo-freetype-font.c | 17 ++- src/cairo-freetype-private.h | 31 +++-- src/cairo-freetype-state.c | 283 +++++++++++++++++++++++++++++++++++------- src/cairo-ft.c | 146 +--------------------- 4 files changed, 267 insertions(+), 210 deletions(-) diff --git a/src/cairo-freetype-font.c b/src/cairo-freetype-font.c index 529137f..94749b2 100644 --- a/src/cairo-freetype-font.c +++ b/src/cairo-freetype-font.c @@ -15,6 +15,14 @@ _cairo_freetype_font_face_destroy (void *abstract_face) { cairo_freetype_font_face_t *font_face = abstract_face; +#ifdef CAIRO_FREETYPE_SUPPORT_UNSAFE + if (font_face->unsafe_face) { + /* the following destroys the unsafe face as well */ + FT_Done_FreeType(font_face->unsafe_face->glyph->library); + font_face->unsafe_face = NULL; + } +#endif + if (font_face == NULL) return; @@ -84,6 +92,10 @@ cairo_freetype_font_face_create (const char* pathname, font_face->load_flags = load_flags & ~FT_LOAD_TARGET_(font_face->hint_mode); font_face->extra_flags = extra_flags; +#ifdef CAIRO_FREETYPE_SUPPORT_UNSAFE + font_face->unsafe_face = NULL; +#endif + if (font_options) font_face->font_options = font_options[0]; else @@ -228,11 +240,6 @@ _cairo_freetype_font_face_scaled_font_create (void *abstra font_matrix, ctm, options, &cairo_freetype_scaled_font_backend); -#ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT - scaled_font->unsafe_ft_face = NULL; - scaled_font->unsafe_count = 0; -#endif - scaled_font->has_shape = _cairo_freetype_compute_transform (&scaled_font->base.scale, &scaled_font->x_scale, &scaled_font->y_scale, diff --git a/src/cairo-freetype-private.h b/src/cairo-freetype-private.h index 5f64f45..b3f8e8e 100644 --- a/src/cairo-freetype-private.h +++ b/src/cairo-freetype-private.h @@ -32,6 +32,23 @@ typedef struct _cairo_freetype_font_face { cairo_freetype_face_flags_t extra_flags; cairo_font_options_t font_options; +#ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT + /* the following is only used to implement the *broken* + * cairo_ft_scaled_lock_face() API. It is a *copy* + * of the font face's FT_Face, which was created in its + * own custom FT_Library, and can thus be used concurrently + * with other Cairo text-related functions + * + * if two threads try to use the same unsafe face at the same + * time, they'll probably run into trouble, but this will not + * ruin Cairo's FreeType state. + * + * the unsafe FT_Face is created on demand, and is only destroyed + * with the font face. + */ + FT_Face unsafe_face; +#endif + } cairo_freetype_font_face_t; @@ -52,20 +69,6 @@ typedef struct _cairo_freetype_scaled_font_t { int ft_x_dpi; int ft_y_dpi; -#ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT - /* despite its name, the following is a *copy* of the - * parent's font_face's FT_Face that was created in its - * own FT_Library instance. this allows any thread - * to use the unsafe_ft_face concurrently from Cairo - * - * on the other hand, this is bad because creating the - * FT_Face takes time, and it also uses a lot more heap - * memory... - */ - FT_Face unsafe_ft_face; - int unsafe_count; -#endif - } cairo_freetype_scaled_font_t; #define CAIRO_FREETYPE_SCALED_FONT__LOAD_FLAGS(f) \ diff --git a/src/cairo-freetype-state.c b/src/cairo-freetype-state.c index 22fa2d1..0bf4b3e 100644 --- a/src/cairo-freetype-state.c +++ b/src/cairo-freetype-state.c @@ -727,17 +727,17 @@ _cairo_freetype_state_destroy (cairo_freetype_state_t *state) free (state); } -static void +static cairo_freetype_state_t* _cairo_freetype_state_create (void) { cairo_freetype_state_t *state = calloc( sizeof(*state), 1 ); if (state == NULL) - return; + return NULL; if ( FT_Init_FreeType( &state->library ) != 0 ) { free (state); - return; + return NULL; } _cairo_mru_faces_list_init (&state->faces, state->library, &state->sizes); @@ -745,9 +745,59 @@ _cairo_freetype_state_create (void) state->foreign_sizes = NULL; - _cairo_freetype_state = state; + return state; +} + + +static cairo_ft_face_t* +_cairo_freetype_state_get_ft_face (cairo_freetype_state_t *state, + cairo_freetype_font_face_t *font_face) +{ + cairo_ft_face_t *face_node; + cairo_ft_face_t key; + + key.path = font_face->path; + key.index = font_face->index; + + _cairo_ft_face_hash (&key); + + face_node = (cairo_ft_face_t*) _cairo_mru_list_find (&state->faces, (cairo_mru_t*)&key); + if (face_node != NULL) { + _cairo_ft_face_ref (face_node); + } + return face_node; +} + + +static cairo_ft_size_t* +_cairo_freetype_state_get_ft_size (cairo_freetype_state_t *state, + cairo_ft_face_t *face_node, + cairo_freetype_scaled_font_t *scaled) +{ + cairo_ft_size_t *size_node; + cairo_ft_size_t key; + + key.face = face_node; + key.width = scaled->ft_char_width; + key.height = scaled->ft_char_height; + key.pixel_sizes = scaled->ft_pixel_sizes; + if (!key.pixel_sizes) { + key.x_dpi = scaled->ft_x_dpi; + key.y_dpi = scaled->ft_y_dpi; + } + + _cairo_ft_size_hash (&key); + + size_node = (cairo_ft_size_t*) _cairo_mru_list_find (&state->sizes, (cairo_mru_t*) &key); + if (size_node != NULL) + _cairo_ft_size_ref (size_node); + + return size_node; } + + + /** * cairo_freetype_state_lock: * @@ -777,7 +827,7 @@ cairo_freetype_state_lock (void) CAIRO_MUTEX_LOCK (_cairo_freetype_state_mutex); if (_cairo_freetype_state == NULL) { - _cairo_freetype_state_create (); + _cairo_freetype_state = _cairo_freetype_state_create (); if (_cairo_freetype_state == NULL) { CAIRO_MUTEX_UNLOCK (_cairo_freetype_state_mutex); return NULL; @@ -787,25 +837,6 @@ cairo_freetype_state_lock (void) } slim_hidden_def (cairo_freetype_state_lock); -static cairo_ft_face_t* -_cairo_freetype_state_get_ft_face (cairo_freetype_state_t *state, - cairo_freetype_font_face_t *font_face) -{ - cairo_ft_face_t *face_node; - cairo_ft_face_t key; - - key.path = font_face->path; - key.index = font_face->index; - - _cairo_ft_face_hash (&key); - - face_node = (cairo_ft_face_t*) _cairo_mru_list_find (&state->faces, (cairo_mru_t*)&key); - if (face_node != NULL) { - _cairo_ft_face_ref (face_node); - } - return face_node; -} - /** * cairo_freetype_state_get_face: @@ -856,6 +887,7 @@ cairo_freetype_state_get_face (cairo_freetype_state_t *state, } slim_hidden_def (cairo_freetype_state_get_face); + /** * cairo_freetype_state_get_size: * @state: Cairo FreeType state handle @@ -917,32 +949,16 @@ cairo_freetype_state_get_size (cairo_freetype_state_t *state, { cairo_ft_face_t *face_node; cairo_ft_size_t *size_node; - cairo_ft_size_t key; face_node = _cairo_freetype_state_get_ft_face (state, font_face); if (face_node == NULL) return state->faces.status; - key.face = face_node; - key.width = scaled->ft_char_width; - key.height = scaled->ft_char_height; - key.pixel_sizes = scaled->ft_pixel_sizes; - if (!key.pixel_sizes) { - key.x_dpi = scaled->ft_x_dpi; - key.y_dpi = scaled->ft_y_dpi; - } - - _cairo_ft_size_hash (&key); - - size_node = (cairo_ft_size_t*) _cairo_mru_list_find (&state->sizes, (cairo_mru_t*) &key); + size_node = _cairo_freetype_state_get_ft_size (state, face_node, scaled); + _cairo_ft_face_unref (face_node); - if (size_node == NULL) { - _cairo_ft_face_unref (face_node); + if (size_node == NULL) return state->sizes.status; - } - - _cairo_ft_size_ref (size_node); - _cairo_ft_face_unref (face_node); *p_size = size_node->size; } @@ -951,10 +967,9 @@ cairo_freetype_state_get_size (cairo_freetype_state_t *state, /* set the shape transform from the scaled font face */ { - FT_Face face = (*p_size)->face; - cairo_freetype_scaled_font_t *scaled = _cairo_scaled_font_to_freetype (scaled_font); + FT_Face face = (*p_size)->face; - if (scaled && scaled->has_shape) + if (scaled->has_shape) FT_Set_Transform (face, &scaled->ft_shape, NULL); else FT_Set_Transform (face, NULL, NULL); @@ -1055,3 +1070,179 @@ _cairo_freetype_font_reset_static_data (void) } CAIRO_MUTEX_UNLOCK(_cairo_freetype_state_mutex); } + + +#ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT + +#include "cairo-ft.h" +slim_hidden_proto (cairo_ft_scaled_font_lock_face); +slim_hidden_proto (cairo_ft_scaled_font_unlock_face); + + +CAIRO_MUTEX_DECLARE (_cairo_freetype_unsafe_mutex); + +/* note that only the creation/destruction of the following is protected by the mutex !! */ +static cairo_freetype_state_t *_cairo_freetype_unsafe_state; + + +static cairo_freetype_state_t* +_cairo_freetype_unsafe_get (void) +{ + cairo_freetype_state_t *state; + + CAIRO_MUTEX_LOCK (_cairo_freetype_unsafe_mutex); + state = _cairo_freetype_unsafe_state; + if (state == NULL) { + state = _cairo_freetype_unsafe_state = _cairo_freetype_state_create (); + } + CAIRO_MUTEX_UNLOCK (_cairo_freetype_unsafe_mutex); + + return state; +} + +/** + * cairo_ft_scaled_font_lock_face: + * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an + * object can be created by calling cairo_scaled_font_create() on a + * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), + * cairo_ft_font_face_create_for_face()). + * + * cairo_ft_font_lock_face() gets the #FT_Face object from a FreeType + * backend font and scales it appropriately for the font. You must + * release the face with cairo_ft_font_unlock_face() + * when you are done using it. Since the #FT_Face object can be + * shared between multiple #cairo_scaled_font_t objects, you must not + * lock any other font objects until you unlock this one. A count is + * kept of the number of times cairo_ft_font_lock_face() is + * called. cairo_ft_font_unlock_face() must be called the same number + * of times. + * + * THIS FUNCTION IS DEPRECATED AND UNSAFE WHEN USED IN MULTI-THREADED + * PROGRAMS. CONSIDER USING cairo_freetype_state_lock() and related + * functions to access FT_Face and/or FT_Size objects instead !! + * + * Return value: The #FT_Face object for @font, scaled appropriately, + * or %NULL if @scaled_font is in an error state (see + * cairo_scaled_font_status()) or there is insufficient memory. + **/ +FT_Face +cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) +{ + cairo_freetype_state_t* unsafe_state; + cairo_freetype_scaled_font_t *scaled_font; + cairo_freetype_font_face_t *font_face; + FT_Face unsafe_face = NULL; + + unsafe_state = _cairo_freetype_unsafe_get(); + if (unsafe_state == NULL) + return NULL; + + scaled_font = _cairo_scaled_font_to_freetype(abstract_font); + if (scaled_font == NULL) + return NULL; + + font_face = (cairo_freetype_font_face_t*) scaled_font->base.font_face; + + if (font_face->foreign_face) + { + unsafe_face = font_face->foreign_face; + + if (scaled_font->ft_pixel_sizes) + FT_Set_Pixel_Sizes (unsafe_face, scaled_font->ft_char_width, scaled_font->ft_char_height); + else + FT_Set_Char_Size (unsafe_face, scaled_font->ft_char_width, scaled_font->ft_char_height, + scaled_font->ft_x_dpi, scaled_font->ft_y_dpi); + } + else + { + cairo_ft_face_t *face_node; + cairo_ft_size_t *size_node; + + face_node = _cairo_freetype_state_get_ft_face (unsafe_state, font_face); + if (face_node == NULL) + return NULL; + + size_node = _cairo_freetype_state_get_ft_size (unsafe_state, face_node, scaled_font); + _cairo_ft_face_unref (face_node); + + if (size_node == NULL) + return NULL; + + FT_Activate_Size (size_node->size); + + unsafe_face = size_node->size->face; + } + + FT_Set_Transform (unsafe_face, &scaled_font->ft_shape, NULL); + + return unsafe_face; +} +slim_hidden_def (cairo_ft_scaled_font_lock_face); + +/** + * cairo_ft_scaled_font_unlock_face: + * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an + * object can be created by calling cairo_scaled_font_create() on a + * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), + * cairo_ft_font_face_create_for_ft_face()). + * + * Releases a face obtained with cairo_ft_scaled_font_lock_face(). + **/ +void +cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) +{ + /* we need to de-reference the size node corresponding to this scaled font + */ + cairo_freetype_state_t *unsafe_state = _cairo_freetype_unsafe_get(); + cairo_freetype_scaled_font_t *scaled_font; + cairo_freetype_font_face_t *font_face; + cairo_ft_face_t *face_node; + cairo_ft_size_t *size_node; + + if (unsafe_state == NULL) + return; + + scaled_font = _cairo_scaled_font_to_freetype (abstract_font); + if (scaled_font == NULL) + return; + + font_face = (cairo_freetype_font_face_t*) scaled_font->base.font_face; + + if (font_face->foreign_face) /* nothing to do here */ + return; + + /* we need to find the corresponding cairo_ft_size_t and dereference it */ + face_node = _cairo_freetype_state_get_ft_face (unsafe_state, font_face); + if (face_node == NULL) + return; + + size_node = _cairo_freetype_state_get_ft_size (unsafe_state, face_node, scaled_font); + _cairo_ft_face_unref (face_node); + + if (size_node != NULL) + { + /* note: the first unref is to undo the ref from get_ft_size() + * the second is to undo the ref from cairo_ft_scaled_font_lock_face() + */ + _cairo_ft_size_unref (size_node); + _cairo_ft_size_unref (size_node); + } +} +slim_hidden_def (cairo_ft_scaled_font_unlock_face); + +#else + + +FT_Face +cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) +{ + return NULL; +} + +void +cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) +{ +} +slim_hidden_def (cairo_ft_scaled_font_unlock_face); + +#endif /* CAIRO_FREETYPE_UNSAFE_SUPPORT */ diff --git a/src/cairo-ft.c b/src/cairo-ft.c index 4908be8..02bd394 100644 --- a/src/cairo-ft.c +++ b/src/cairo-ft.c @@ -10,151 +10,7 @@ slim_hidden_proto (cairo_fontconfig_font_options_substitute); cairo_public void cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font); -/** - * cairo_ft_scaled_font_lock_face: - * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an - * object can be created by calling cairo_scaled_font_create() on a - * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), - * cairo_ft_font_face_create_for_face()). - * - * cairo_ft_font_lock_face() gets the #FT_Face object from a FreeType - * backend font and scales it appropriately for the font. You must - * release the face with cairo_ft_font_unlock_face() - * when you are done using it. Since the #FT_Face object can be - * shared between multiple #cairo_scaled_font_t objects, you must not - * lock any other font objects until you unlock this one. A count is - * kept of the number of times cairo_ft_font_lock_face() is - * called. cairo_ft_font_unlock_face() must be called the same number - * of times. - * - * THIS FUNCTION IS DEPRECATED AND UNSAFE WHEN USED IN MULTI-THREADED - * PROGRAMS. CONSIDER USING cairo_freetype_state_lock() and related - * functions to access FT_Face and/or FT_Size objects instead !! - * - * Return value: The #FT_Face object for @font, scaled appropriately, - * or %NULL if @scaled_font is in an error state (see - * cairo_scaled_font_status()) or there is insufficient memory. - **/ -FT_Face -cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) -{ -#ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT - /* we do something absolutely hideous, but that guarantees that - * clients will never have problems: we create a new FT_Library - * instance, and *copy* the content of the original FT_Face into - * it... - */ - cairo_freetype_scaled_font_t *scaled_font; - cairo_freetype_font_face_t *font_face; - FT_Library library; - FT_Face unsafe_face = NULL; - - scaled_font = _cairo_scaled_font_to_freetype(abstract_font); - if (scaled_font == NULL) - return NULL; - - CAIRO_MUTEX_LOCK (scaled_font->base.mutex); - if (scaled_font->unsafe_ft_face) { - unsafe_face = scaled_font->unsafe_ft_face; - goto EXIT; - } - - font_face = (cairo_freetype_font_face_t*) scaled_font->base.font_face; - - if ( FT_Init_FreeType (&library) != 0 ) - goto EXIT; - - if (font_face->foreign_face == NULL) - { - /* easy copy: grab the (path,index) pair and re-open the font */ - /* if FT_New_Face fails, unsafe_face will be set to NULL */ - FT_New_Face (library, font_face->path, font_face->index, &unsafe_face); - } - else - { - /* this is a *lot* more tricky, though pretty much rare */ - /* as far as we're concerned, this unsafe API is only used */ - /* by Pango on Unix, where FreeType streams are always memory-mapped */ - /* by default. */ - /* */ - /* Note that in theory, you'll likely crash your application if you */ - /* destroy the parent font face before the locked FT_Face, but this */ - /* should be seen as just punishment for using this brain-dead */ - /* API anyway */ - /* */ - FT_Face org_face = font_face->foreign_face; - FT_Stream stream = org_face->stream; - - if (stream->base == NULL || stream->size == 0) - goto EXIT; - - /* if this call fails, unsafe_face is set to NULL */ - FT_New_Memory_Face (library, stream->base, stream->size, - org_face->face_index, &unsafe_face); - } - -EXIT: - scaled_font->unsafe_ft_face = unsafe_face; - - - /* scale the face */ - if (unsafe_face != NULL) { - scaled_font->unsafe_count++; - - if (scaled_font->ft_pixel_sizes) - FT_Set_Pixel_Sizes (unsafe_face, - scaled_font->ft_char_width, - scaled_font->ft_char_height ); - else - FT_Set_Char_Size (unsafe_face, - scaled_font->ft_char_width, - scaled_font->ft_char_height, - scaled_font->ft_x_dpi, - scaled_font->ft_y_dpi); - - if (scaled_font->has_shape) - FT_Set_Transform (unsafe_face, &scaled_font->ft_shape, NULL); - } - - CAIRO_MUTEX_UNLOCK (scaled_font->base.mutex); - return unsafe_face; -#else - return NULL; -#endif -} - -/** - * cairo_ft_scaled_font_unlock_face: - * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an - * object can be created by calling cairo_scaled_font_create() on a - * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), - * cairo_ft_font_face_create_for_ft_face()). - * - * Releases a face obtained with cairo_ft_scaled_font_lock_face(). - **/ -void -cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) -{ -#ifdef CAIRO_FREETYPE_UNSAFE_SUPPORT - cairo_freetype_scaled_font_t *scaled_font; - FT_Face face; - - scaled_font = _cairo_scaled_font_to_freetype (abstract_font); - if (scaled_font == NULL) - return; - - CAIRO_MUTEX_LOCK (scaled_font->base.mutex); - face = scaled_font->unsafe_ft_face; - if (face != NULL && --scaled_font->unsafe_count <= 0) { - /* the following automatically destroyes the FT_Face */ - FT_Done_FreeType (face->glyph->library); - scaled_font->unsafe_ft_face = NULL; - scaled_font->unsafe_count = 0; /* just in case */ - } - CAIRO_MUTEX_UNLOCK (scaled_font->base.mutex); -#endif -} - +CAIRO_MUTEX_DECLARE(_cairo_ft_unsafe_lock); /** * cairo_ft_font_face_create_for_ft_face: -- 1.5.0 From 8b9271f84a2a40726c96545e599dd9e10cd5c086 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 28 Feb 2007 22:32:15 +0100 Subject: [PATCH] implement brain-dead font options management to mimic original Cairo behaviour --- src/cairo-freetype-font.c | 7 +++++++ src/cairo-freetype-private.h | 15 +++++++++++++++ src/cairo-ft.c | 9 ++++++++- 3 files changed, 30 insertions(+), 1 deletions(-) diff --git a/src/cairo-freetype-font.c b/src/cairo-freetype-font.c index 94749b2..3b46a7c 100644 --- a/src/cairo-freetype-font.c +++ b/src/cairo-freetype-font.c @@ -422,6 +422,13 @@ _cairo_freetype_scaled_glyph_init (void *abstract_font, cairo_font_options_t options = scaled_font->base.options; cairo_font_options_t *face_options = &font_face->font_options; +#ifdef CAIRO_FREETYPE_BRAINDEAD_OPTIONS + if (font_face->ignore_user_options) { + options.antialias = face_options->antialias; + options.subpixel_order = face_options->subpixel_order; + } +#endif + if (options.antialias == CAIRO_ANTIALIAS_DEFAULT) options.antialias = face_options->antialias; diff --git a/src/cairo-freetype-private.h b/src/cairo-freetype-private.h index b3f8e8e..7dcfcdc 100644 --- a/src/cairo-freetype-private.h +++ b/src/cairo-freetype-private.h @@ -12,6 +12,13 @@ CAIRO_BEGIN_DECLS */ #define CAIRO_FREETYPE_UNSAFE_SUPPORT +/* define CAIRO_FREETYPE_BRAINDEAD_OPTIONS if you want to support the original + * Cairo behaviour of *ignoring* user-provided font options when creating a + * scaled font from a font face created through a FontConfig pattern, using + * the options from the original pattern instead + */ +#define CAIRO_FREETYPE_BRAINDEAD_OPTIONS + /* the FreeType font face holds either a (path,index) pair, or a foreign FT_Face * object that was created by the client, and thus belongs to a different FT_Library * instance. @@ -49,6 +56,14 @@ typedef struct _cairo_freetype_font_face { FT_Face unsafe_face; #endif +#ifdef CAIRO_FREETYPE_BRAINDEAD_OPTIONS + /* when the following is defined, the user provided font options + * will be ignored. this is to comply with Cairo's weird behaviour + * in its original FontConfig font backend + */ + cairo_bool_t ignore_user_options; +#endif + } cairo_freetype_font_face_t; diff --git a/src/cairo-ft.c b/src/cairo-ft.c index 02bd394..0deb969 100644 --- a/src/cairo-ft.c +++ b/src/cairo-ft.c @@ -103,5 +103,12 @@ cairo_ft_font_options_substitute (const cairo_font_options_t *options, cairo_font_face_t * cairo_ft_font_face_create_for_pattern (FcPattern *pattern) { - return cairo_fontconfig_font_face_create (pattern); + cairo_font_face_t* font_face; + + font_face = cairo_fontconfig_font_face_create (pattern); +#ifdef CAIRO_FREETYPE_BRAINDEAD_OPTIONS + if (font_face) + ((cairo_freetype_font_face_t*)font_face)->ignore_user_options = 1; +#endif + return font_face; } -- 1.5.0 From abb06ec70a73a4ef2059e722eafa4653f7d68cce Mon Sep 17 00:00:00 2001 From: David Turner Date: Sun, 4 Mar 2007 17:34:23 +0100 Subject: [PATCH] fix the FreeType based toy interface --- src/cairo-fontconfig.c | 5 ++- src/cairo-freetype-font.c | 48 ++++++++++++++++++++++++++--------------- src/cairo-freetype-private.h | 25 +++++++++++++++++---- src/cairo-freetype-state.c | 12 +++++----- 4 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/cairo-fontconfig.c b/src/cairo-fontconfig.c index 84510de..7207d1f 100644 --- a/src/cairo-fontconfig.c +++ b/src/cairo-fontconfig.c @@ -296,8 +296,9 @@ _cairo_fontconfig_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, if (!font_face) goto FREE_RESOLVED; - status = _cairo_freetype_font_face_scaled_font_create - (font_face, font_matrix, ctm, font_options, font); + status = _cairo_freetype_font_face_scaled_font_create_ + (&toy_face->base, font_face, font_matrix, ctm, font_options, + font); cairo_font_face_destroy (font_face); diff --git a/src/cairo-freetype-font.c b/src/cairo-freetype-font.c index 3b46a7c..2f55c51 100644 --- a/src/cairo-freetype-font.c +++ b/src/cairo-freetype-font.c @@ -195,10 +195,7 @@ _cairo_font_face_to_freetype (cairo_font_face_t *font_face) cairo_freetype_scaled_font_t* _cairo_scaled_font_to_freetype (cairo_scaled_font_t *scaled_font) { - /* IMPORTANT: do note check the scaled_font->backend, since both - * FreeType and FontConfig scaled fonts share the same font face implementation - */ - if (!scaled_font || !_cairo_font_face_to_freetype (scaled_font->font_face)) + if (!scaled_font || scaled_font->backend->type != CAIRO_FONT_TYPE_FT) return NULL; return (cairo_freetype_scaled_font_t*) scaled_font; @@ -217,13 +214,14 @@ _cairo_freetype_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font) cairo_status_t -_cairo_freetype_font_face_scaled_font_create (void *abstract_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **pscaled_font) +_cairo_freetype_font_face_scaled_font_create_ (void *abstract_face, + cairo_font_face_t *real_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **pscaled_font) { - cairo_freetype_font_face_t *font_face = abstract_face; + cairo_font_face_t *font_face = abstract_face; cairo_freetype_scaled_font_t *scaled_font = NULL; cairo_freetype_state_t *state = NULL; cairo_status_t status = CAIRO_STATUS_SUCCESS; @@ -235,8 +233,10 @@ _cairo_freetype_font_face_scaled_font_create (void *abstra goto Exit; } - _cairo_scaled_font_init (&scaled_font->base, - (cairo_font_face_t*) font_face, + /* by default, use the parent face as the real face */ + scaled_font->real_face = cairo_font_face_reference (real_face); + + _cairo_scaled_font_init (&scaled_font->base, font_face, font_matrix, ctm, options, &cairo_freetype_scaled_font_backend); @@ -254,7 +254,7 @@ _cairo_freetype_font_face_scaled_font_create (void *abstra } /* compute the scaling parameters needed by cairo_freetype_state_get_size */ - status = cairo_freetype_state_get_face (state, &font_face->base, &ft_face); + status = cairo_freetype_state_get_face (state, real_face, &ft_face); if (status) goto Exit; @@ -372,7 +372,7 @@ Exit: cairo_freetype_state_unlock (state); if (status && scaled_font) { - cairo_scaled_font_destroy (&scaled_font->base); + _cairo_scaled_font_fini (&scaled_font->base); free (scaled_font); scaled_font = NULL; } @@ -383,12 +383,24 @@ Exit: } +cairo_private cairo_status_t +_cairo_freetype_font_face_scaled_font_create (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **scaled_font) +{ + return _cairo_freetype_font_face_scaled_font_create_ + (abstract_face, abstract_face, font_matrix, ctm, options, scaled_font); +} void _cairo_freetype_scaled_font_fini (void *abstract_font) { - /* we don't have anything special to do here */ + cairo_freetype_scaled_font_t *scaled_font = abstract_font; + + cairo_font_face_destroy (scaled_font->real_face); } @@ -418,7 +430,7 @@ _cairo_freetype_scaled_glyph_init (void *abstract_font, cairo_scaled_glyph_info_t info) { cairo_freetype_scaled_font_t *scaled_font = abstract_font; - cairo_freetype_font_face_t *font_face = _cairo_font_face_to_freetype (scaled_font->base.font_face); + cairo_freetype_font_face_t *font_face = _cairo_font_face_to_freetype (scaled_font->real_face); cairo_font_options_t options = scaled_font->base.options; cairo_font_options_t *face_options = &font_face->font_options; @@ -464,7 +476,7 @@ _cairo_freetype_ucs4_to_index (void *abstract_font, { FT_Face face; - if ( !cairo_freetype_state_get_face (state, scaled_font->base.font_face, &face) ) + if ( !cairo_freetype_state_get_face (state, scaled_font->real_face, &face) ) result = FT_Get_Char_Index (face, ucs4); cairo_freetype_state_unlock (state); @@ -495,7 +507,7 @@ _cairo_freetype_load_truetype_table (void *abstract_font, if (state == NULL) return CAIRO_STATUS_NO_MEMORY; - status = cairo_freetype_state_get_face (state, scaled_font->base.font_face, &face); + status = cairo_freetype_state_get_face (state, scaled_font->real_face, &face); if (!status) { #if HAVE_FT_LOAD_SFNT_TABLE diff --git a/src/cairo-freetype-private.h b/src/cairo-freetype-private.h index 7dcfcdc..91ee9d3 100644 --- a/src/cairo-freetype-private.h +++ b/src/cairo-freetype-private.h @@ -18,7 +18,8 @@ CAIRO_BEGIN_DECLS * the options from the original pattern instead */ #define CAIRO_FREETYPE_BRAINDEAD_OPTIONS - + + /* the FreeType font face holds either a (path,index) pair, or a foreign FT_Face * object that was created by the client, and thus belongs to a different FT_Library * instance. @@ -84,6 +85,12 @@ typedef struct _cairo_freetype_scaled_font_t { int ft_x_dpi; int ft_y_dpi; + /* the following points to a cairo_freetype_font_face_t + * corresponding to the real data when the parent is a + * toy font face. it is equal to base.font_face otherwise + */ + cairo_font_face_t *real_face; + } cairo_freetype_scaled_font_t; #define CAIRO_FREETYPE_SCALED_FONT__LOAD_FLAGS(f) \ @@ -100,6 +107,18 @@ typedef struct _cairo_freetype_scaled_font_t { cairo_private cairo_freetype_font_face_t* _cairo_font_face_to_freetype (cairo_font_face_t *font_face); +/* dynamic cast to a freetype scaled font, or NULL */ +cairo_private cairo_freetype_scaled_font_t* +_cairo_scaled_font_to_freetype (cairo_scaled_font_t *scaled_font); + +cairo_private cairo_status_t +_cairo_freetype_font_face_scaled_font_create_ (void *abstract_face, + cairo_font_face_t *real_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **scaled_font); + cairo_private cairo_status_t _cairo_freetype_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, @@ -107,10 +126,6 @@ _cairo_freetype_font_face_scaled_font_create (void *abstra const cairo_font_options_t *options, cairo_scaled_font_t **scaled_font); -/* dynamic cast to a freetype scaled font, or NULL */ -cairo_private cairo_freetype_scaled_font_t* -_cairo_scaled_font_to_freetype (cairo_scaled_font_t *scaled_font); - /* this function is used to compute the FreeType-specific * scaling and transform parameters corresponding to a diff --git a/src/cairo-freetype-state.c b/src/cairo-freetype-state.c index 0bf4b3e..59e75c9 100644 --- a/src/cairo-freetype-state.c +++ b/src/cairo-freetype-state.c @@ -914,15 +914,15 @@ cairo_freetype_state_get_size (cairo_freetype_state_t *state, cairo_freetype_font_face_t *font_face; cairo_freetype_scaled_font_t *scaled; - if (scaled_font == NULL) + + scaled = _cairo_scaled_font_to_freetype (scaled_font); + if (scaled == NULL) return CAIRO_STATUS_NULL_POINTER; - font_face = _cairo_font_face_to_freetype (scaled_font->font_face); + font_face = _cairo_font_face_to_freetype (scaled->real_face); if (font_face == NULL) return CAIRO_STATUS_NO_MEMORY; /* XXX: find better error code ? */ - scaled = (cairo_freetype_scaled_font_t*) scaled_font; - if (font_face->foreign_face) { cairo_freetype_size_ref_t *foreign_size; @@ -1141,7 +1141,7 @@ cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) if (scaled_font == NULL) return NULL; - font_face = (cairo_freetype_font_face_t*) scaled_font->base.font_face; + font_face = (cairo_freetype_font_face_t*) scaled_font->real_face; if (font_face->foreign_face) { @@ -1206,7 +1206,7 @@ cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) if (scaled_font == NULL) return; - font_face = (cairo_freetype_font_face_t*) scaled_font->base.font_face; + font_face = (cairo_freetype_font_face_t*) scaled_font->real_face; if (font_face->foreign_face) /* nothing to do here */ return; -- 1.5.0 From 93dbcd84f20ecd862fdfe6e921ffee79e503a61f Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 5 Mar 2007 07:55:23 +0100 Subject: [PATCH] fix another freetype state bug that could create an infinite loop in rare cases !! --- src/cairo-freetype-state.c | 19 +++++++++++++++++++ 1 files changed, 19 insertions(+), 0 deletions(-) diff --git a/src/cairo-freetype-state.c b/src/cairo-freetype-state.c index 59e75c9..fabc6f5 100644 --- a/src/cairo-freetype-state.c +++ b/src/cairo-freetype-state.c @@ -337,6 +337,24 @@ _cairo_mru_list_remove_select (cairo_mru_list_t *list, cairo_mru_equal_func_t equal_func, void *equal_key) { +#if 1 + cairo_bool_t is_last; + cairo_mru_t *node, *next; + + next = list->free_nodes; + is_last = (next == NULL); + while (!is_last) { + node = next; + next = node->next; + is_last = (next == list->free_nodes); + + if ( (*equal_func) (node, (cairo_mru_t*)equal_key) ) { + _cairo_mru_remove (&list->free_nodes, node); + list->num_nodes--; + _cairo_mru_list_free_node (list, node); + } + } +#else cairo_mru_t *first, *node; first = list->free_nodes; @@ -355,6 +373,7 @@ _cairo_mru_list_remove_select (cairo_mru_list_t *list, } while (node != first); } +#endif } -- 1.5.0 From 1105a0c8a092e7a01f25caa5c8a17de684b64d90 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 5 Mar 2007 18:25:31 +0100 Subject: [PATCH] add copyright and license information to the head of all new files --- src/cairo-fontconfig.c | 35 +++++++++++++++++++++++++++++++++++ src/cairo-fontconfig.h | 35 +++++++++++++++++++++++++++++++++++ src/cairo-freetype-font.c | 39 +++++++++++++++++++++++++++++++++++++++ src/cairo-freetype-glyph.c | 39 +++++++++++++++++++++++++++++++++++++++ src/cairo-freetype-private.h | 35 +++++++++++++++++++++++++++++++++++ src/cairo-freetype-state.c | 36 ++++++++++++++++++++++++++++++++++++ src/cairo-freetype.h | 35 +++++++++++++++++++++++++++++++++++ src/cairo-ft.c | 35 +++++++++++++++++++++++++++++++++++ 8 files changed, 289 insertions(+), 0 deletions(-) diff --git a/src/cairo-fontconfig.c b/src/cairo-fontconfig.c index 7207d1f..fb300b6 100644 --- a/src/cairo-fontconfig.c +++ b/src/cairo-fontconfig.c @@ -1,3 +1,38 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 University of Southern California + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * David Turner + */ #include "cairo-fontconfig.h" #include "cairo-freetype-private.h" diff --git a/src/cairo-fontconfig.h b/src/cairo-fontconfig.h index 89eb059..c309406 100644 --- a/src/cairo-fontconfig.h +++ b/src/cairo-fontconfig.h @@ -1,3 +1,38 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 University of Southern California + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * David Turner + */ #ifndef CAIRO_FONTCONFIG_H #define CAIRO_FONTCONFIG_H diff --git a/src/cairo-freetype-font.c b/src/cairo-freetype-font.c index 2f55c51..a2e8058 100644 --- a/src/cairo-freetype-font.c +++ b/src/cairo-freetype-font.c @@ -1,3 +1,42 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 University of Southern California + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * David Turner + * Graydon Hoare + * Owen Taylor + * Keith Packard + * Carl Worth + */ #include "cairo-freetype-private.h" #include #include FT_TRUETYPE_TABLES_H diff --git a/src/cairo-freetype-glyph.c b/src/cairo-freetype-glyph.c index b143866..06429ee 100644 --- a/src/cairo-freetype-glyph.c +++ b/src/cairo-freetype-glyph.c @@ -1,3 +1,42 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 University of Southern California + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * David Turner + * Graydon Hoare + * Owen Taylor + * Keith Packard + * Carl Worth + */ #include "cairo-freetype-private.h" #include FT_OUTLINE_H #if HAVE_FT_GLYPHSLOT_EMBOLDEN diff --git a/src/cairo-freetype-private.h b/src/cairo-freetype-private.h index 91ee9d3..fddf8e3 100644 --- a/src/cairo-freetype-private.h +++ b/src/cairo-freetype-private.h @@ -1,3 +1,38 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 University of Southern California + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * David Turner + */ #ifndef _CAIRO_FREETYPE_PRIVATE_H #define _CAIRO_FREETYPE_PRIVATE_H diff --git a/src/cairo-freetype-state.c b/src/cairo-freetype-state.c index fabc6f5..517a7a5 100644 --- a/src/cairo-freetype-state.c +++ b/src/cairo-freetype-state.c @@ -1,3 +1,39 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 University of Southern California + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * David Turner + */ + /* the following source is used to maintain Cairo's FreeType "state" * * the state contains a single FT_Library handle, as well as two MRU-ordered diff --git a/src/cairo-freetype.h b/src/cairo-freetype.h index b13d79a..d35413d 100644 --- a/src/cairo-freetype.h +++ b/src/cairo-freetype.h @@ -1,3 +1,38 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 University of Southern California + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * David Turner + */ #ifndef CAIRO_FREETYPE_H #define CAIRO_FREETYPE_H diff --git a/src/cairo-ft.c b/src/cairo-ft.c index 0deb969..1c165dc 100644 --- a/src/cairo-ft.c +++ b/src/cairo-ft.c @@ -1,3 +1,38 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 University of Southern California + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * David Turner + */ #include "cairo-ft.h" #include "cairo-freetype.h" #include "cairo-fontconfig.h" -- 1.5.0 From aafc24fc1a3bf3f7e67675cfba0d2023fb183865 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 5 Mar 2007 19:10:26 +0100 Subject: [PATCH] fix unhinted FreeType glyph advance computations to use the more accurate linearHoriAdvance/linearVertAdvance fields --- src/cairo-freetype-glyph.c | 38 ++++++++++++++++++++++++++++---------- 1 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/cairo-freetype-glyph.c b/src/cairo-freetype-glyph.c index 06429ee..d52bfff 100644 --- a/src/cairo-freetype-glyph.c +++ b/src/cairo-freetype-glyph.c @@ -44,6 +44,7 @@ #endif #define DOUBLE_FROM_26_6(x) ((x)/64.0) +#define DOUBLE_FROM_16_16(x) ((x)/65536.0) /* the following code is related to glyph loading and rendering with FreeType * due to its length and complexity, it has been separated from the rest of @@ -932,22 +933,39 @@ _cairo_freetype_scaled_glyph_init_raw (cairo_freetype_scaled_font_t *scaled_fon fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor; } } else { + double x_advance, y_advance, x_bearing, y_bearing; + fs_metrics.width = DOUBLE_FROM_26_6 (metrics->width) * x_factor; fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor; - if (!vertical_layout) { - fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; - fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor; + if (!vertical_layout) + { + x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX); + y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY); - fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor; - fs_metrics.y_advance = 0 * y_factor; - } else { - fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor; - fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor; + if (font_options->hint_metrics == CAIRO_HINT_METRICS_OFF) + x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance); + else + x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance); + + y_advance = 0.; + } + else + { + x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX); + y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY); - fs_metrics.x_advance = 0 * x_factor; - fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor; + x_advance = 0.; + + if (font_options->hint_metrics == CAIRO_HINT_METRICS_OFF) + y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance); + else + y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance); } + fs_metrics.x_bearing = x_bearing * x_factor; + fs_metrics.y_bearing = y_bearing * y_factor; + fs_metrics.x_advance = x_advance * x_factor; + fs_metrics.y_advance = y_advance * y_factor; } _cairo_scaled_glyph_set_metrics (scaled_glyph, -- 1.5.0