diff options
author | Behdad Esfahbod <behdad@behdad.org> | 2008-05-09 16:01:37 +0200 |
---|---|---|
committer | Behdad Esfahbod <behdad@behdad.org> | 2008-05-10 01:21:45 +0200 |
commit | 17f21ea3071ef5d7833c615271b18ae35673c349 (patch) | |
tree | db75342c60e470ee6598fb87222493200f40deb5 | |
parent | 9f2971440b59c311d88beeeb31e1d456489b107d (diff) |
[cairo-user-font] Implement user fonts
-rw-r--r-- | doc/public/tmpl/cairo-font-face.sgml | 1 | ||||
-rw-r--r-- | doc/public/tmpl/cairo-status.sgml | 3 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/cairo-misc.c | 4 | ||||
-rw-r--r-- | src/cairo-user-font.c | 486 | ||||
-rw-r--r-- | src/cairo.h | 68 | ||||
-rwxr-xr-x | src/cairoint.h | 2 | ||||
-rw-r--r-- | test/.gitignore | 1 | ||||
-rw-r--r-- | test/Makefile.am | 4 | ||||
-rw-r--r-- | test/user-font-pdf-ref.png | bin | 0 -> 4549 bytes | |||
-rw-r--r-- | test/user-font-ref.png | bin | 0 -> 4681 bytes | |||
-rw-r--r-- | test/user-font-svg-ref.png | bin | 0 -> 4642 bytes | |||
-rw-r--r-- | test/user-font.c | 236 |
13 files changed, 803 insertions, 3 deletions
diff --git a/doc/public/tmpl/cairo-font-face.sgml b/doc/public/tmpl/cairo-font-face.sgml index 2dc66741..1bb686d7 100644 --- a/doc/public/tmpl/cairo-font-face.sgml +++ b/doc/public/tmpl/cairo-font-face.sgml @@ -69,6 +69,7 @@ cairo_get_font_face(). @CAIRO_FONT_TYPE_FT: @CAIRO_FONT_TYPE_WIN32: @CAIRO_FONT_TYPE_QUARTZ: +@CAIRO_FONT_TYPE_USER: <!-- ##### FUNCTION cairo_font_face_get_type ##### --> <para> diff --git a/doc/public/tmpl/cairo-status.sgml b/doc/public/tmpl/cairo-status.sgml index 9b2bbe12..143ce81e 100644 --- a/doc/public/tmpl/cairo-status.sgml +++ b/doc/public/tmpl/cairo-status.sgml @@ -63,6 +63,9 @@ code is required before or after each individual cairo function call. @CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: @CAIRO_STATUS_TEMP_FILE_ERROR: @CAIRO_STATUS_INVALID_STRIDE: +@CAIRO_STATUS_FONT_TYPE_MISMATCH: +@CAIRO_STATUS_USER_FONT_IMMUTABLE: +@CAIRO_STATUS_USER_FONT_ERROR: <!-- ##### FUNCTION cairo_status_to_string ##### --> <para> diff --git a/src/Makefile.am b/src/Makefile.am index e5382d33..c52a7a09 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -63,6 +63,7 @@ cairo_base_sources = \ cairo-surface-fallback-private.h \ cairo-surface-private.h \ cairo-traps.c \ + cairo-user-font.c \ cairo-unicode.c \ cairo-output-stream.c \ cairo-output-stream-private.h \ diff --git a/src/cairo-misc.c b/src/cairo-misc.c index b85d98f5..02c88737 100644 --- a/src/cairo-misc.c +++ b/src/cairo-misc.c @@ -103,6 +103,10 @@ cairo_status_to_string (cairo_status_t status) return "invalid value for stride"; case CAIRO_STATUS_FONT_TYPE_MISMATCH: return "the font type is not appropriate for the operation"; + case CAIRO_STATUS_USER_FONT_IMMUTABLE: + return "the user-font is immutable"; + case CAIRO_STATUS_USER_FONT_ERROR: + return "error occurred in a user-font callback function"; } return "<unknown error status>"; diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c new file mode 100644 index 00000000..6422d1bc --- /dev/null +++ b/src/cairo-user-font.c @@ -0,0 +1,486 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2006, 2008 Red Hat, Inc + * + * 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 Red Hat, Inc. + * + * Contributor(s): + * Kristian Høgsberg <krh@redhat.com> + * Behdad Esfahbod <behdad@behdad.org> + */ + +#include "cairoint.h" +#include "cairo-meta-surface-private.h" +#include "cairo-analysis-surface-private.h" + +typedef struct _cairo_user_scaled_font_methods { + cairo_user_scaled_font_init_func_t init; + cairo_user_scaled_font_render_glyph_func_t render_glyph; + cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph; + cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs; +} cairo_user_scaled_font_methods_t; + +typedef struct _cairo_user_font_face { + cairo_font_face_t base; + + /* Set to true after first scaled font is created. At that point, + * the scaled_font_methods cannot change anymore. */ + cairo_bool_t immutable; + + cairo_user_scaled_font_methods_t scaled_font_methods; +} cairo_user_font_face_t; + +typedef struct _cairo_user_scaled_font { + cairo_scaled_font_t base; + + cairo_text_extents_t default_glyph_extents; +} cairo_user_scaled_font_t; + +/* TODO test user fonts using other fonts in the render_glyph */ + +/* #cairo_user_scaled_font_t */ + +const cairo_scaled_font_backend_t cairo_user_scaled_font_backend; + +static cairo_int_status_t +_cairo_user_scaled_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_user_scaled_font_t *scaled_font = abstract_font; + cairo_surface_t *meta_surface = scaled_glyph->meta_surface; + + if (!scaled_glyph->meta_surface) { + cairo_user_font_face_t *face = + (cairo_user_font_face_t *) scaled_font->base.font_face; + cairo_text_extents_t extents = scaled_font->default_glyph_extents; + cairo_t *cr; + + meta_surface = _cairo_meta_surface_create (CAIRO_CONTENT_COLOR_ALPHA, -1, -1); + cr = cairo_create (meta_surface); + + cairo_set_matrix (cr, &scaled_font->base.scale); + cairo_set_font_size (cr, 1.0); + cairo_set_font_options (cr, &scaled_font->base.options); + + status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font, + _cairo_scaled_glyph_index(scaled_glyph), + cr, &extents); + + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_status (cr); + + cairo_destroy (cr); + + if (status) { + cairo_surface_destroy (meta_surface); + return status; + } + + _cairo_scaled_glyph_set_meta_surface (scaled_glyph, + &scaled_font->base, + meta_surface); + + + /* set metrics */ + + if (extents.width == 0.) { + /* Compute extents.x/y/width/height from meta_surface, in font space */ + + cairo_box_t bbox; + double x1, y1, x2, y2; + cairo_surface_t *null_surface = _cairo_null_surface_create (cairo_surface_get_content (meta_surface)); + cairo_surface_t *analysis_surface = _cairo_analysis_surface_create (null_surface, -1, -1); + cairo_surface_destroy (null_surface); + + _cairo_analysis_surface_set_ctm (analysis_surface, &scaled_font->base.scale_inverse); + status = _cairo_meta_surface_replay (meta_surface, analysis_surface); + _cairo_analysis_surface_get_bounding_box (analysis_surface, &bbox); + cairo_surface_destroy (analysis_surface); + + _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2); + + extents.x_bearing = x1; + extents.y_bearing = y1; + extents.width = x2 - x1; + extents.height = y2 - y1; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &extents); + } + + if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { + cairo_surface_t *surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int width, height; + + /* TODO: extend the glyph cache to support argb glyphs */ + + width = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x) - + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) - + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + /* XXX handle / figure out antialias/subpixel font options to choose + * the right format here? */ + surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); + + cairo_surface_set_device_offset (surface, + -scaled_glyph->metrics.x_bearing, + -scaled_glyph->metrics.y_bearing); + status = _cairo_meta_surface_replay (meta_surface, surface); + + if (status) { + cairo_surface_destroy(surface); + return status; + } + + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *) surface); + } + + if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { + cairo_path_fixed_t *path = _cairo_path_fixed_create (); + if (!path) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_meta_surface_get_path (meta_surface, path); + + if (status) { + _cairo_path_fixed_destroy (path); + return status; + } + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + } + + return status; +} + +static unsigned long +_cairo_user_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + cairo_user_scaled_font_t *scaled_font = abstract_font; + cairo_user_font_face_t *face = + (cairo_user_font_face_t *) scaled_font->base.font_face; + unsigned long glyph = 0; + + if (face->scaled_font_methods.unicode_to_glyph) { + cairo_status_t status; + + status = face->scaled_font_methods.unicode_to_glyph (&scaled_font->base, + ucs4, &glyph); + + if (status != CAIRO_STATUS_SUCCESS) { + status = _cairo_scaled_font_set_error (&scaled_font->base, status); + glyph = 0; + } + + } else { + glyph = ucs4; + } + + return glyph; +} + +static cairo_int_status_t +_cairo_user_text_to_glyphs (void *abstract_font, + double x, + double y, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_user_scaled_font_t *scaled_font = abstract_font; + cairo_user_font_face_t *face = + (cairo_user_font_face_t *) scaled_font->base.font_face; + + if (face->scaled_font_methods.text_to_glyphs) { + int i; + + *glyphs = NULL; + + status = face->scaled_font_methods.text_to_glyphs (&scaled_font->base, + utf8, glyphs, num_glyphs); + + if (status != CAIRO_STATUS_SUCCESS) { + status = _cairo_scaled_font_set_error (&scaled_font->base, status); + if (*glyphs) { + free (*glyphs); + *glyphs = NULL; + } + return status; + } + + /* Convert from font space to user space and add x,y */ + for (i = 0; i < *num_glyphs; i++) { + double gx = (*glyphs)[i].x; + double gy = (*glyphs)[i].y; + + cairo_matrix_transform_point (&scaled_font->base.font_matrix, + &gx, &gy); + + (*glyphs)[i].x = gx + x; + (*glyphs)[i].y = gy + y; + } + } + + return status; +} + +const cairo_scaled_font_backend_t cairo_user_scaled_font_backend = { + CAIRO_FONT_TYPE_USER, + NULL, /* create_toy */ + NULL, /* scaled_font_fini */ + _cairo_user_scaled_glyph_init, + _cairo_user_text_to_glyphs, + _cairo_user_ucs4_to_index, + NULL, /* show_glyphs */ + NULL, /* load_truetype_table */ + NULL, /* map_glyphs_to_unicode */ +}; + +/* #cairo_user_font_face_t */ + +static cairo_status_t +_cairo_user_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) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_user_font_face_t *font_face = abstract_face; + cairo_user_scaled_font_t *user_scaled_font = NULL; + cairo_font_extents_t font_extents = {1., 0., 1., 1., 0.}; + + font_face->immutable = TRUE; + + user_scaled_font = malloc (sizeof (cairo_user_scaled_font_t)); + if (user_scaled_font == NULL) + return CAIRO_STATUS_NO_MEMORY; + + status = _cairo_scaled_font_init (&user_scaled_font->base, + &font_face->base, + font_matrix, ctm, options, + &cairo_user_scaled_font_backend); + + if (status) { + free (user_scaled_font); + return status; + } + + if (font_face->scaled_font_methods.init != NULL) + status = font_face->scaled_font_methods.init (&user_scaled_font->base, + &font_extents); + + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_scaled_font_set_metrics (&user_scaled_font->base, &font_extents); + + + if (status != CAIRO_STATUS_SUCCESS) { + _cairo_scaled_font_fini (&user_scaled_font->base); + free (user_scaled_font); + } else { + user_scaled_font->default_glyph_extents.x_bearing = 0.; + user_scaled_font->default_glyph_extents.y_bearing = -font_extents.ascent; + user_scaled_font->default_glyph_extents.width = 0.; + user_scaled_font->default_glyph_extents.height = font_extents.ascent + font_extents.descent; + user_scaled_font->default_glyph_extents.x_advance = font_extents.max_x_advance; + user_scaled_font->default_glyph_extents.y_advance = 0.; /* XXX */ + + *scaled_font = &user_scaled_font->base; + } + + return status; +} + +static const cairo_font_face_backend_t _cairo_user_font_face_backend = { + CAIRO_FONT_TYPE_USER, + NULL, /* destroy */ + _cairo_user_font_face_scaled_font_create +}; + + +/* Implement the public interface */ + +/** + * cairo_user_font_face_create: + * + * TODO: document this + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.8 + **/ +cairo_font_face_t * +cairo_user_font_face_create (void) +{ + cairo_user_font_face_t *font_face; + + font_face = malloc (sizeof (cairo_user_font_face_t)); + if (!font_face) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *)&_cairo_font_face_nil; + } + + _cairo_font_face_init (&font_face->base, &_cairo_user_font_face_backend); + + font_face->immutable = FALSE; + memset (&font_face->scaled_font_methods, 0, sizeof (font_face->scaled_font_methods)); + + return &font_face->base; +} + +/* User-font method setters */ + +static cairo_bool_t +_cairo_font_face_is_user (cairo_font_face_t *font_face) +{ + return font_face->backend == &_cairo_user_font_face_backend; +} + + +void +cairo_user_font_face_set_init_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_init_func_t init_func) +{ + cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face; + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.init = init_func; +} + +void +cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_render_glyph_func_t render_glyph_func) +{ + cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face; + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.render_glyph = render_glyph_func; +} + +void +cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func) +{ + cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face; + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.unicode_to_glyph = unicode_to_glyph_func; +} + +void +cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func) +{ + cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face; + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.text_to_glyphs = text_to_glyphs_func; +} + +/* User-font method getters */ + +cairo_user_scaled_font_init_func_t +cairo_user_font_face_get_init_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face; + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + return user_font_face->scaled_font_methods.init; +} + +cairo_user_scaled_font_render_glyph_func_t +cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face; + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + return user_font_face->scaled_font_methods.render_glyph; +} + +cairo_user_scaled_font_unicode_to_glyph_func_t +cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face; + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + return user_font_face->scaled_font_methods.unicode_to_glyph; +} + +cairo_user_scaled_font_text_to_glyphs_func_t +cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face; + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + return user_font_face->scaled_font_methods.text_to_glyphs; +} diff --git a/src/cairo.h b/src/cairo.h index 4ac8f32c..2116f003 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -204,6 +204,8 @@ typedef struct _cairo_user_data_key { * @CAIRO_STATUS_TEMP_FILE_ERROR: error creating or writing to a temporary file (Since 1.6) * @CAIRO_STATUS_INVALID_STRIDE: invalid value for stride (Since 1.6) * @CAIRO_STATUS_FONT_TYPE_MISMATCH: the font type is not appropriate for the operation (Since 1.8) + * @CAIRO_STATUS_USER_FONT_IMMUTABLE: the user-font is immutable (Since 1.8) + * @CAIRO_STATUS_USER_FONT_ERROR: error occurred in a user-font callback function (Since 1.8) * * #cairo_status_t is used to indicate errors that can occur when * using Cairo. In some cases it is returned directly by functions. @@ -239,7 +241,9 @@ typedef enum _cairo_status { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, CAIRO_STATUS_TEMP_FILE_ERROR, CAIRO_STATUS_INVALID_STRIDE, - CAIRO_STATUS_FONT_TYPE_MISMATCH + CAIRO_STATUS_FONT_TYPE_MISMATCH, + CAIRO_STATUS_USER_FONT_IMMUTABLE, + CAIRO_STATUS_USER_FONT_ERROR /* after adding a new error: update CAIRO_STATUS_LAST_STATUS in cairoint.h */ } cairo_status_t; @@ -1153,12 +1157,14 @@ cairo_font_face_get_reference_count (cairo_font_face_t *font_face); cairo_public cairo_status_t cairo_font_face_status (cairo_font_face_t *font_face); + /** * cairo_font_type_t: * @CAIRO_FONT_TYPE_TOY: The font was created using cairo's toy font api * @CAIRO_FONT_TYPE_FT: The font is of type FreeType * @CAIRO_FONT_TYPE_WIN32: The font is of type Win32 * @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6) + * @CAIRO_FONT_TYPE_USER: The font was create using cairo's user font api (Since: 1.8) * * #cairo_font_type_t is used to describe the type of a given font * face or scaled font. The font types are also known as "font @@ -1193,7 +1199,8 @@ typedef enum _cairo_font_type { CAIRO_FONT_TYPE_TOY, CAIRO_FONT_TYPE_FT, CAIRO_FONT_TYPE_WIN32, - CAIRO_FONT_TYPE_QUARTZ + CAIRO_FONT_TYPE_QUARTZ, + CAIRO_FONT_TYPE_USER } cairo_font_type_t; cairo_public cairo_font_type_t @@ -1272,6 +1279,63 @@ cairo_public void cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, cairo_font_options_t *options); +/* User fonts */ + +cairo_public cairo_font_face_t * +cairo_user_font_face_create (void); + +/* User-font method signatures */ + +typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents); + +typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents); + +typedef cairo_status_t (*cairo_user_scaled_font_unicode_to_glyph_func_t) (cairo_scaled_font_t *scaled_font, + unsigned long unicode, + unsigned long *glyph_index); + +typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_scaled_font_t *scaled_font, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs); + +/* User-font method setters */ + +cairo_public void +cairo_user_font_face_set_init_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_init_func_t init_func); + +cairo_public void +cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_render_glyph_func_t render_glyph_func); + +cairo_public void +cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func); + +cairo_public void +cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func); + +/* User-font method getters */ + +cairo_public cairo_user_scaled_font_init_func_t +cairo_user_font_face_get_init_func (cairo_font_face_t *font_face); + +cairo_public cairo_user_scaled_font_render_glyph_func_t +cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face); + +cairo_public cairo_user_scaled_font_unicode_to_glyph_func_t +cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face); + +cairo_public cairo_user_scaled_font_text_to_glyphs_func_t +cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face); + + /* Query functions */ cairo_public cairo_operator_t diff --git a/src/cairoint.h b/src/cairoint.h index 6df8170f..b1c0ad75 100755 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -110,7 +110,7 @@ _cairo_win32_tmpfile (void); * a bit of a pain, but it should be easy to always catch as long as * one adds a new test case to test a trigger of the new status value. */ -#define CAIRO_STATUS_LAST_STATUS CAIRO_STATUS_FONT_TYPE_MISMATCH +#define CAIRO_STATUS_LAST_STATUS CAIRO_STATUS_USER_FONT_ERROR /* Size in bytes of buffer to use off the stack per functions. diff --git a/test/.gitignore b/test/.gitignore index cf617408..65f9a77d 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -202,6 +202,7 @@ truetype-tables unantialiased-shapes unbounded-operator user-data +user-font xlib-expose-event xlib-surface xlib-surface-source diff --git a/test/Makefile.am b/test/Makefile.am index 99a7eceb..88cc1ed8 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -157,6 +157,7 @@ truetype-tables$(EXEEXT) \ unantialiased-shapes$(EXEEXT) \ unbounded-operator$(EXEEXT) \ user-data$(EXEEXT) \ +user-font$(EXEEXT) \ zero-alpha$(EXEEXT) # XXX: Here are some existing tests that are currently disabled for @@ -695,6 +696,9 @@ REFERENCE_IMAGES = \ unantialiased-shapes-quartz-ref.png \ unbounded-operator-ref.png \ unbounded-operator-rgb24-ref.png \ + user-font-ref.png \ + user-font-pdf-ref.png \ + user-font-svg-ref.png \ unbounded-operator-quartz-ref.png \ unbounded-operator-quartz-rgb24-ref.png \ xlib-expose-event-ref.png \ diff --git a/test/user-font-pdf-ref.png b/test/user-font-pdf-ref.png Binary files differnew file mode 100644 index 00000000..babca100 --- /dev/null +++ b/test/user-font-pdf-ref.png diff --git a/test/user-font-ref.png b/test/user-font-ref.png Binary files differnew file mode 100644 index 00000000..00c6d43e --- /dev/null +++ b/test/user-font-ref.png diff --git a/test/user-font-svg-ref.png b/test/user-font-svg-ref.png Binary files differnew file mode 100644 index 00000000..96c53ef7 --- /dev/null +++ b/test/user-font-svg-ref.png diff --git a/test/user-font.c b/test/user-font.c new file mode 100644 index 00000000..3cdf7bc9 --- /dev/null +++ b/test/user-font.c @@ -0,0 +1,236 @@ +/* + * Copyright © 2006, 2008 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Red Hat, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Red Hat, Inc. makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Contributor(s): + * Kristian Høgsberg <krh@redhat.com> + * Behdad Esfahbod <behdad@behdad.org> + */ + +#include <stdlib.h> +#include <stdio.h> + +#include "cairo-test.h" + +/*#define ROTATED 1*/ + +#define BORDER 10 +#define TEXT_SIZE 64 +#define WIDTH (TEXT_SIZE * 9.75 + 2*BORDER) +#define HEIGHT ((TEXT_SIZE + 2*BORDER)*2) +#define TEXT "cairo user font" + +static cairo_test_draw_function_t draw; + +cairo_test_t test = { + "user-font", + "Tests user font feature", +#ifndef ROTATED + WIDTH, HEIGHT, +#else + WIDTH, WIDTH, +#endif + draw +}; + +/* Simple glyph definition: 1 - 15 means lineto (or moveto for first + * point) for one of the points on this grid: + * + * 1 2 3 + * 4 5 6 + * 7 8 9 + * ----10 11 12----(baseline) + * 13 14 15 + */ + +#define END_GLYPH 0 +#define STROKE 13 +#define CLOSE 14 + +static const struct { + unsigned long ucs4; + int width; + char data[16]; +} glyphs [] = { + { -1 , 0, { END_GLYPH } }, + { ' ', 1, { END_GLYPH } }, + { 'c', 3, { 6, 4, 10, 12, STROKE, END_GLYPH } }, + { 'a', 3, { 4, 6, 12, 10, 7, 8, STROKE, END_GLYPH } }, + { 'i', 1, { 1, 1, STROKE, 4, 10, STROKE, END_GLYPH } }, + { 'r', 3, { 4, 10, STROKE, 7, 5, 6, STROKE, END_GLYPH } }, + { 'o', 3, { 4, 10, 12, 6, CLOSE, END_GLYPH } }, + { 'u', 3, { 4, 10, 12, 6, STROKE, END_GLYPH } }, + { 's', 3, { 6, 4, 7, 9, 12, 10, STROKE, END_GLYPH } }, + { 'e', 3, { 12, 10, 4, 6, 9, 8, STROKE, END_GLYPH } }, + { 'f', 3, { 3, 2, 11, STROKE, 4, 6, STROKE, END_GLYPH } }, + { 'n', 3, { 10, 4, STROKE, 7, 5, 6, 12, STROKE, END_GLYPH } }, + { 't', 3, { 2, 11, 12, STROKE, 4, 6, STROKE, END_GLYPH } }, + { 'h', 3, { 1, 10, STROKE, 7, 5, 6, 12, STROKE, END_GLYPH } }, + { 'l', 1, { 1, 10, STROKE, END_GLYPH } }, + { '-', 2, { 7, 8, STROKE, END_GLYPH } }, + { '\0', 0, { END_GLYPH } }, +}; + +static cairo_status_t +test_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *metrics) +{ + metrics->ascent = .75; + metrics->descent = .25; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +test_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font, + unsigned long unicode, + unsigned long *glyph) +{ + int i; + + for (i = 0; glyphs[i].ucs4 != '\0'; i++) + if (glyphs[i].ucs4 == unicode) { + *glyph = i; + return CAIRO_STATUS_SUCCESS; + } + + /* Fall through and default to undefined glyph. */ + return CAIRO_STATUS_INVALID_INDEX; +} + +static cairo_status_t +test_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *metrics) +{ + int i; + const char *data; + div_t d; + double x, y; + + metrics->x_advance = glyphs[glyph].width / 4.0; + + cairo_set_line_width (cr, 0.1); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + + data = glyphs[glyph].data; + for (i = 0; data[i] != END_GLYPH; i++) { + switch (data[i]) { + case STROKE: + cairo_new_sub_path (cr); + break; + + case CLOSE: + cairo_close_path (cr); + break; + + default: + d = div (data[i] - 1, 3); + x = d.rem / 4.0 + 0.125; + y = d.quot / 5.0 + 0.4 - 1.0; + cairo_line_to (cr, x, y); + } + } + cairo_stroke (cr); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_font_face_t * +get_user_font_face (void) +{ + static cairo_font_face_t *user_font_face = NULL; + + if (!user_font_face) { + user_font_face = cairo_user_font_face_create (); + cairo_user_font_face_set_init_func (user_font_face, test_scaled_font_init); + cairo_user_font_face_set_render_glyph_func (user_font_face, test_scaled_font_render_glyph); + cairo_user_font_face_set_unicode_to_glyph_func (user_font_face, test_scaled_font_unicode_to_glyph); + } + + return user_font_face; +} + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + const char text[] = TEXT; + cairo_font_extents_t font_extents; + cairo_text_extents_t extents; + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + +#ifdef ROTATED + cairo_translate (cr, TEXT_SIZE, 0); + cairo_rotate (cr, .6); +#endif + + cairo_set_font_face (cr, get_user_font_face ()); + cairo_set_font_size (cr, TEXT_SIZE); + + cairo_font_extents (cr, &font_extents); + cairo_text_extents (cr, text, &extents); + + /* logical boundaries in red */ + cairo_move_to (cr, 0, BORDER); + cairo_rel_line_to (cr, WIDTH, 0); + cairo_move_to (cr, 0, BORDER + font_extents.ascent); + cairo_rel_line_to (cr, WIDTH, 0); + cairo_move_to (cr, 0, BORDER + font_extents.ascent + font_extents.descent); + cairo_rel_line_to (cr, WIDTH, 0); + cairo_move_to (cr, BORDER, 0); + cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE); + cairo_move_to (cr, BORDER + extents.x_advance, 0); + cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE); + cairo_set_source_rgba (cr, 1, 0, 0, .8); + cairo_set_line_width (cr, 2); + cairo_stroke (cr); + + /* ink boundaries in green */ + cairo_rectangle (cr, + BORDER + extents.x_bearing, BORDER + font_extents.ascent + extents.y_bearing, + extents.width, extents.height); + cairo_set_source_rgba (cr, 0, 1, 0, .8); + cairo_set_line_width (cr, 2); + cairo_stroke (cr); + + /* text in gray */ + cairo_set_source_rgba (cr, 0, 0, 0, 0.5); + cairo_move_to (cr, BORDER, BORDER + font_extents.ascent); + cairo_show_text (cr, text); + + + /* filled version of text in light blue */ + cairo_set_source_rgba (cr, 0, 0, 1, 0.5); + cairo_move_to (cr, BORDER, BORDER + font_extents.height + 2*BORDER + font_extents.ascent); + cairo_text_path (cr, text); + cairo_fill (cr); + + return CAIRO_TEST_SUCCESS; +} + +int +main (void) +{ + return cairo_test (&test); +} |