summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBehdad Esfahbod <behdad@behdad.org>2008-05-09 16:01:37 +0200
committerBehdad Esfahbod <behdad@behdad.org>2008-05-10 01:21:45 +0200
commit17f21ea3071ef5d7833c615271b18ae35673c349 (patch)
treedb75342c60e470ee6598fb87222493200f40deb5
parent9f2971440b59c311d88beeeb31e1d456489b107d (diff)
[cairo-user-font] Implement user fonts
-rw-r--r--doc/public/tmpl/cairo-font-face.sgml1
-rw-r--r--doc/public/tmpl/cairo-status.sgml3
-rw-r--r--src/Makefile.am1
-rw-r--r--src/cairo-misc.c4
-rw-r--r--src/cairo-user-font.c486
-rw-r--r--src/cairo.h68
-rwxr-xr-xsrc/cairoint.h2
-rw-r--r--test/.gitignore1
-rw-r--r--test/Makefile.am4
-rw-r--r--test/user-font-pdf-ref.pngbin0 -> 4549 bytes
-rw-r--r--test/user-font-ref.pngbin0 -> 4681 bytes
-rw-r--r--test/user-font-svg-ref.pngbin0 -> 4642 bytes
-rw-r--r--test/user-font.c236
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
new file mode 100644
index 00000000..babca100
--- /dev/null
+++ b/test/user-font-pdf-ref.png
Binary files differ
diff --git a/test/user-font-ref.png b/test/user-font-ref.png
new file mode 100644
index 00000000..00c6d43e
--- /dev/null
+++ b/test/user-font-ref.png
Binary files differ
diff --git a/test/user-font-svg-ref.png b/test/user-font-svg-ref.png
new file mode 100644
index 00000000..96c53ef7
--- /dev/null
+++ b/test/user-font-svg-ref.png
Binary files differ
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);
+}