summaryrefslogtreecommitdiff
path: root/src/cairo-font-face-twin.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-font-face-twin.c')
-rw-r--r--src/cairo-font-face-twin.c752
1 files changed, 752 insertions, 0 deletions
diff --git a/src/cairo-font-face-twin.c b/src/cairo-font-face-twin.c
new file mode 100644
index 000000000..2ad263028
--- /dev/null
+++ b/src/cairo-font-face-twin.c
@@ -0,0 +1,752 @@
+/*
+ * Copyright © 2004 Keith Packard
+ * Copyright © 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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 Keith Packard
+ *
+ * Contributor(s):
+ * Keith Packard <keithp@keithp.com>
+ * Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+#include <math.h>
+
+/*
+ * This file implements a user-font rendering the descendant of the Hershey
+ * font coded by Keith Packard for use in the Twin window system.
+ * The actual font data is in cairo-font-face-twin-data.c
+ *
+ * Ported to cairo user font and extended by Behdad Esfahbod.
+ */
+
+
+
+static cairo_user_data_key_t twin_properties_key;
+
+
+/*
+ * Face properties
+ */
+
+/* We synthesize multiple faces from the twin data. Here is the parameters. */
+
+/* The following tables and matching code are copied from Pango */
+
+/* CSS weight */
+typedef enum {
+ TWIN_WEIGHT_THIN = 100,
+ TWIN_WEIGHT_ULTRALIGHT = 200,
+ TWIN_WEIGHT_LIGHT = 300,
+ TWIN_WEIGHT_BOOK = 380,
+ TWIN_WEIGHT_NORMAL = 400,
+ TWIN_WEIGHT_MEDIUM = 500,
+ TWIN_WEIGHT_SEMIBOLD = 600,
+ TWIN_WEIGHT_BOLD = 700,
+ TWIN_WEIGHT_ULTRABOLD = 800,
+ TWIN_WEIGHT_HEAVY = 900,
+ TWIN_WEIGHT_ULTRAHEAVY = 1000
+} twin_face_weight_t;
+
+/* CSS stretch */
+typedef enum {
+ TWIN_STRETCH_ULTRA_CONDENSED,
+ TWIN_STRETCH_EXTRA_CONDENSED,
+ TWIN_STRETCH_CONDENSED,
+ TWIN_STRETCH_SEMI_CONDENSED,
+ TWIN_STRETCH_NORMAL,
+ TWIN_STRETCH_SEMI_EXPANDED,
+ TWIN_STRETCH_EXPANDED,
+ TWIN_STRETCH_EXTRA_EXPANDED,
+ TWIN_STRETCH_ULTRA_EXPANDED
+} twin_face_stretch_t;
+
+typedef struct
+{
+ int value;
+ const char str[16];
+} FieldMap;
+
+static const FieldMap slant_map[] = {
+ { CAIRO_FONT_SLANT_NORMAL, "" },
+ { CAIRO_FONT_SLANT_NORMAL, "Roman" },
+ { CAIRO_FONT_SLANT_OBLIQUE, "Oblique" },
+ { CAIRO_FONT_SLANT_ITALIC, "Italic" }
+};
+
+static const FieldMap smallcaps_map[] = {
+ { FALSE, "" },
+ { TRUE, "Small-Caps" }
+};
+
+static const FieldMap weight_map[] = {
+ { TWIN_WEIGHT_THIN, "Thin" },
+ { TWIN_WEIGHT_ULTRALIGHT, "Ultra-Light" },
+ { TWIN_WEIGHT_ULTRALIGHT, "Extra-Light" },
+ { TWIN_WEIGHT_LIGHT, "Light" },
+ { TWIN_WEIGHT_BOOK, "Book" },
+ { TWIN_WEIGHT_NORMAL, "" },
+ { TWIN_WEIGHT_NORMAL, "Regular" },
+ { TWIN_WEIGHT_MEDIUM, "Medium" },
+ { TWIN_WEIGHT_SEMIBOLD, "Semi-Bold" },
+ { TWIN_WEIGHT_SEMIBOLD, "Demi-Bold" },
+ { TWIN_WEIGHT_BOLD, "Bold" },
+ { TWIN_WEIGHT_ULTRABOLD, "Ultra-Bold" },
+ { TWIN_WEIGHT_ULTRABOLD, "Extra-Bold" },
+ { TWIN_WEIGHT_HEAVY, "Heavy" },
+ { TWIN_WEIGHT_HEAVY, "Black" },
+ { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" },
+ { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Heavy" },
+ { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Black" },
+ { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Black" }
+};
+
+static const FieldMap stretch_map[] = {
+ { TWIN_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" },
+ { TWIN_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" },
+ { TWIN_STRETCH_CONDENSED, "Condensed" },
+ { TWIN_STRETCH_SEMI_CONDENSED, "Semi-Condensed" },
+ { TWIN_STRETCH_NORMAL, "" },
+ { TWIN_STRETCH_SEMI_EXPANDED, "Semi-Expanded" },
+ { TWIN_STRETCH_EXPANDED, "Expanded" },
+ { TWIN_STRETCH_EXTRA_EXPANDED, "Extra-Expanded" },
+ { TWIN_STRETCH_ULTRA_EXPANDED, "Ultra-Expanded" }
+};
+
+static const FieldMap monospace_map[] = {
+ { FALSE, "" },
+ { TRUE, "Mono" },
+ { TRUE, "Monospace" }
+};
+
+
+typedef struct _twin_face_properties {
+ cairo_font_slant_t slant;
+ twin_face_weight_t weight;
+ twin_face_stretch_t stretch;
+
+ /* lets have some fun */
+ cairo_bool_t monospace;
+ cairo_bool_t smallcaps;
+} twin_face_properties_t;
+
+static cairo_bool_t
+field_matches (const char *s1,
+ const char *s2,
+ int len)
+{
+ int c1, c2;
+
+ while (len && *s1 && *s2)
+ {
+#define TOLOWER(c) \
+ (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
+
+ c1 = TOLOWER (*s1);
+ c2 = TOLOWER (*s2);
+ if (c1 != c2) {
+ if (c1 == '-') {
+ s1++;
+ continue;
+ }
+ return FALSE;
+ }
+ s1++; s2++;
+ len--;
+ }
+
+ return len == 0 && *s1 == '\0';
+}
+
+static cairo_bool_t
+parse_int (const char *word,
+ size_t wordlen,
+ int *out)
+{
+ char *end;
+ long val = strtol (word, &end, 10);
+ int i = val;
+
+ if (end != word && (end == word + wordlen) && val >= 0 && val == i)
+ {
+ if (out)
+ *out = i;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static cairo_bool_t
+find_field (const char *what,
+ const FieldMap *map,
+ int n_elements,
+ const char *str,
+ int len,
+ int *val)
+{
+ int i;
+ cairo_bool_t had_prefix = FALSE;
+
+ if (what)
+ {
+ i = strlen (what);
+ if (len > i && 0 == strncmp (what, str, i) && str[i] == '=')
+ {
+ str += i + 1;
+ len -= i + 1;
+ had_prefix = TRUE;
+ }
+ }
+
+ for (i=0; i<n_elements; i++)
+ {
+ if (map[i].str[0] && field_matches (map[i].str, str, len))
+ {
+ if (val)
+ *val = map[i].value;
+ return TRUE;
+ }
+ }
+
+ if (!what || had_prefix)
+ return parse_int (str, len, val);
+
+ return FALSE;
+}
+
+static void
+parse_field (twin_face_properties_t *props,
+ const char *str,
+ int len)
+{
+ if (field_matches ("Normal", str, len))
+ return;
+
+#define FIELD(NAME) \
+ if (find_field (STRINGIFY (NAME), NAME##_map, ARRAY_LENGTH (NAME##_map), str, len, \
+ (int *)(void *)&props->NAME)) \
+ return; \
+
+ FIELD (weight);
+ FIELD (slant);
+ FIELD (stretch);
+ FIELD (smallcaps);
+ FIELD (monospace);
+
+#undef FIELD
+}
+
+static void
+face_props_parse (twin_face_properties_t *props,
+ const char *s)
+{
+ const char *start, *end;
+
+ for (start = end = s; *end; end++) {
+ if (*end != ' ' && *end != ':')
+ continue;
+
+ if (start < end)
+ parse_field (props, start, end - start);
+ start = end + 1;
+ }
+ if (start < end)
+ parse_field (props, start, end - start);
+}
+
+static twin_face_properties_t *
+twin_font_face_create_properties (cairo_font_face_t *twin_face)
+{
+ twin_face_properties_t *props;
+
+ props = malloc (sizeof (twin_face_properties_t));
+ if (unlikely (props == NULL))
+ return NULL;
+
+ props->stretch = TWIN_STRETCH_NORMAL;
+ props->slant = CAIRO_FONT_SLANT_NORMAL;
+ props->weight = TWIN_WEIGHT_NORMAL;
+ props->monospace = FALSE;
+ props->smallcaps = FALSE;
+
+ if (unlikely (cairo_font_face_set_user_data (twin_face,
+ &twin_properties_key,
+ props, free))) {
+ free (props);
+ return NULL;
+ }
+
+ return props;
+}
+
+static cairo_status_t
+twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face,
+ cairo_toy_font_face_t *toy_face)
+{
+ twin_face_properties_t *props;
+
+ props = twin_font_face_create_properties (twin_face);
+ if (unlikely (props == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ props->slant = toy_face->slant;
+ props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ?
+ TWIN_WEIGHT_NORMAL : TWIN_WEIGHT_BOLD;
+ face_props_parse (props, toy_face->family);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+/*
+ * Scaled properties
+ */
+
+typedef struct _twin_scaled_properties {
+ twin_face_properties_t *face_props;
+
+ cairo_bool_t snap; /* hint outlines */
+
+ double weight; /* unhinted pen width */
+ double penx, peny; /* hinted pen width */
+ double marginl, marginr; /* hinted side margins */
+
+ double stretch; /* stretch factor */
+} twin_scaled_properties_t;
+
+static void
+compute_hinting_scale (cairo_t *cr,
+ double x, double y,
+ double *scale, double *inv)
+{
+ cairo_user_to_device_distance (cr, &x, &y);
+ *scale = x == 0 ? y : y == 0 ? x :sqrt (x*x + y*y);
+ *inv = 1 / *scale;
+}
+
+static void
+compute_hinting_scales (cairo_t *cr,
+ double *x_scale, double *x_scale_inv,
+ double *y_scale, double *y_scale_inv)
+{
+ double x, y;
+
+ x = 1; y = 0;
+ compute_hinting_scale (cr, x, y, x_scale, x_scale_inv);
+
+ x = 0; y = 1;
+ compute_hinting_scale (cr, x, y, y_scale, y_scale_inv);
+}
+
+#define SNAPXI(p) (_cairo_round ((p) * x_scale) * x_scale_inv)
+#define SNAPYI(p) (_cairo_round ((p) * y_scale) * y_scale_inv)
+
+/* This controls the global font size */
+#define F(g) ((g) / 72.)
+
+static void
+twin_hint_pen_and_margins(cairo_t *cr,
+ double *penx, double *peny,
+ double *marginl, double *marginr)
+{
+ double x_scale, x_scale_inv;
+ double y_scale, y_scale_inv;
+ double margin;
+
+ compute_hinting_scales (cr,
+ &x_scale, &x_scale_inv,
+ &y_scale, &y_scale_inv);
+
+ *penx = SNAPXI (*penx);
+ if (*penx < x_scale_inv)
+ *penx = x_scale_inv;
+
+ *peny = SNAPYI (*peny);
+ if (*peny < y_scale_inv)
+ *peny = y_scale_inv;
+
+ margin = *marginl + *marginr;
+ *marginl = SNAPXI (*marginl);
+ if (*marginl < x_scale_inv)
+ *marginl = x_scale_inv;
+
+ *marginr = margin - *marginl;
+ if (*marginr < 0)
+ *marginr = 0;
+ *marginr = SNAPXI (*marginr);
+}
+
+static cairo_status_t
+twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font,
+ cairo_t *cr)
+{
+ cairo_status_t status;
+ twin_scaled_properties_t *props;
+
+ props = malloc (sizeof (twin_scaled_properties_t));
+ if (unlikely (props == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+
+ props->face_props = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+ &twin_properties_key);
+
+ props->snap = scaled_font->options.hint_style > CAIRO_HINT_STYLE_NONE;
+
+ /* weight */
+ props->weight = props->face_props->weight * (F (4) / TWIN_WEIGHT_NORMAL);
+
+ /* pen & margins */
+ props->penx = props->peny = props->weight;
+ props->marginl = props->marginr = F (4);
+ if (scaled_font->options.hint_style > CAIRO_HINT_STYLE_SLIGHT)
+ twin_hint_pen_and_margins(cr,
+ &props->penx, &props->peny,
+ &props->marginl, &props->marginr);
+
+ /* stretch */
+ props->stretch = 1 + .1 * ((int) props->face_props->stretch - (int) TWIN_STRETCH_NORMAL);
+
+
+ /* Save it */
+ status = cairo_scaled_font_set_user_data (scaled_font,
+ &twin_properties_key,
+ props, free);
+ if (unlikely (status))
+ goto FREE_PROPS;
+
+ return CAIRO_STATUS_SUCCESS;
+
+FREE_PROPS:
+ free (props);
+ return status;
+}
+
+
+/*
+ * User-font implementation
+ */
+
+static cairo_status_t
+twin_scaled_font_init (cairo_scaled_font_t *scaled_font,
+ cairo_t *cr,
+ cairo_font_extents_t *metrics)
+{
+ metrics->ascent = F (54);
+ metrics->descent = 1 - metrics->ascent;
+
+ return twin_scaled_font_compute_properties (scaled_font, cr);
+}
+
+#define TWIN_GLYPH_MAX_SNAP_X 4
+#define TWIN_GLYPH_MAX_SNAP_Y 7
+
+typedef struct {
+ int n_snap_x;
+ int8_t snap_x[TWIN_GLYPH_MAX_SNAP_X];
+ double snapped_x[TWIN_GLYPH_MAX_SNAP_X];
+ int n_snap_y;
+ int8_t snap_y[TWIN_GLYPH_MAX_SNAP_Y];
+ double snapped_y[TWIN_GLYPH_MAX_SNAP_Y];
+} twin_snap_info_t;
+
+#define twin_glyph_left(g) ((g)[0])
+#define twin_glyph_right(g) ((g)[1])
+#define twin_glyph_ascent(g) ((g)[2])
+#define twin_glyph_descent(g) ((g)[3])
+
+#define twin_glyph_n_snap_x(g) ((g)[4])
+#define twin_glyph_n_snap_y(g) ((g)[5])
+#define twin_glyph_snap_x(g) (&g[6])
+#define twin_glyph_snap_y(g) (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g))
+#define twin_glyph_draw(g) (twin_glyph_snap_y(g) + twin_glyph_n_snap_y(g))
+
+static void
+twin_compute_snap (cairo_t *cr,
+ twin_snap_info_t *info,
+ const signed char *b)
+{
+ int s, n;
+ const signed char *snap;
+ double x_scale, x_scale_inv;
+ double y_scale, y_scale_inv;
+
+ compute_hinting_scales (cr,
+ &x_scale, &x_scale_inv,
+ &y_scale, &y_scale_inv);
+
+ snap = twin_glyph_snap_x (b);
+ n = twin_glyph_n_snap_x (b);
+ info->n_snap_x = n;
+ assert (n <= TWIN_GLYPH_MAX_SNAP_X);
+ for (s = 0; s < n; s++) {
+ info->snap_x[s] = snap[s];
+ info->snapped_x[s] = SNAPXI (F (snap[s]));
+ }
+
+ snap = twin_glyph_snap_y (b);
+ n = twin_glyph_n_snap_y (b);
+ info->n_snap_y = n;
+ assert (n <= TWIN_GLYPH_MAX_SNAP_Y);
+ for (s = 0; s < n; s++) {
+ info->snap_y[s] = snap[s];
+ info->snapped_y[s] = SNAPYI (F (snap[s]));
+ }
+}
+
+static double
+twin_snap (int8_t v, int n, int8_t *snap, double *snapped)
+{
+ int s;
+
+ if (!n)
+ return F(v);
+
+ if (snap[0] == v)
+ return snapped[0];
+
+ for (s = 0; s < n - 1; s++)
+ {
+ if (snap[s+1] == v)
+ return snapped[s+1];
+
+ if (snap[s] <= v && v <= snap[s+1])
+ {
+ int before = snap[s];
+ int after = snap[s+1];
+ int dist = after - before;
+ double snap_before = snapped[s];
+ double snap_after = snapped[s+1];
+ double dist_before = v - before;
+ return snap_before + (snap_after - snap_before) * dist_before / dist;
+ }
+ }
+ return F(v);
+}
+
+#define SNAPX(p) twin_snap (p, info.n_snap_x, info.snap_x, info.snapped_x)
+#define SNAPY(p) twin_snap (p, info.n_snap_y, info.snap_y, info.snapped_y)
+
+static cairo_status_t
+twin_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font,
+ unsigned long glyph,
+ cairo_t *cr,
+ cairo_text_extents_t *metrics)
+{
+ double x1, y1, x2, y2, x3, y3;
+ double marginl;
+ twin_scaled_properties_t *props;
+ twin_snap_info_t info;
+ const int8_t *b;
+ const int8_t *g;
+ int8_t w;
+ double gw;
+
+ props = cairo_scaled_font_get_user_data (scaled_font, &twin_properties_key);
+
+ /* Save glyph space, we need it when stroking */
+ cairo_save (cr);
+
+ /* center the pen */
+ cairo_translate (cr, props->penx * .5, -props->peny * .5);
+
+ /* small-caps */
+ if (props->face_props->smallcaps && glyph >= 'a' && glyph <= 'z') {
+ glyph += 'A' - 'a';
+ /* 28 and 42 are small and capital letter heights of the glyph data */
+ cairo_scale (cr, 1, 28. / 42);
+ }
+
+ /* slant */
+ if (props->face_props->slant != CAIRO_FONT_SLANT_NORMAL) {
+ cairo_matrix_t shear = { 1, 0, -.2, 1, 0, 0};
+ cairo_transform (cr, &shear);
+ }
+
+ b = _cairo_twin_outlines +
+ _cairo_twin_charmap[unlikely (glyph >= ARRAY_LENGTH (_cairo_twin_charmap)) ? 0 : glyph];
+ g = twin_glyph_draw(b);
+ w = twin_glyph_right(b);
+ gw = F(w);
+
+ marginl = props->marginl;
+
+ /* monospace */
+ if (props->face_props->monospace) {
+ double monow = F(24);
+ double extra = props->penx + props->marginl + props->marginr;
+ cairo_scale (cr, (monow + extra) / (gw + extra), 1);
+ gw = monow;
+
+ /* resnap margin for new transform */
+ {
+ double x, y, x_scale, x_scale_inv;
+ x = 1; y = 0;
+ compute_hinting_scale (cr, x, y, &x_scale, &x_scale_inv);
+ marginl = SNAPXI (marginl);
+ }
+ }
+
+ cairo_translate (cr, marginl, 0);
+
+ /* stretch */
+ cairo_scale (cr, props->stretch, 1);
+
+ if (props->snap)
+ twin_compute_snap (cr, &info, b);
+ else
+ info.n_snap_x = info.n_snap_y = 0;
+
+ /* advance width */
+ metrics->x_advance = gw * props->stretch + props->penx + props->marginl + props->marginr;
+
+ /* glyph shape */
+ for (;;) {
+ switch (*g++) {
+ case 'M':
+ cairo_close_path (cr);
+ /* fall through */
+ case 'm':
+ x1 = SNAPX(*g++);
+ y1 = SNAPY(*g++);
+ cairo_move_to (cr, x1, y1);
+ continue;
+ case 'L':
+ cairo_close_path (cr);
+ /* fall through */
+ case 'l':
+ x1 = SNAPX(*g++);
+ y1 = SNAPY(*g++);
+ cairo_line_to (cr, x1, y1);
+ continue;
+ case 'C':
+ cairo_close_path (cr);
+ /* fall through */
+ case 'c':
+ x1 = SNAPX(*g++);
+ y1 = SNAPY(*g++);
+ x2 = SNAPX(*g++);
+ y2 = SNAPY(*g++);
+ x3 = SNAPX(*g++);
+ y3 = SNAPY(*g++);
+ cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
+ continue;
+ case 'E':
+ cairo_close_path (cr);
+ /* fall through */
+ case 'e':
+ cairo_restore (cr); /* restore glyph space */
+ cairo_set_tolerance (cr, 0.01);
+ cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width (cr, 1);
+ cairo_scale (cr, props->penx, props->peny);
+ cairo_stroke (cr);
+ break;
+ case 'X':
+ /* filler */
+ continue;
+ }
+ break;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font,
+ unsigned long unicode,
+ unsigned long *glyph)
+{
+ /* We use an identity charmap. Which means we could live
+ * with no unicode_to_glyph method too. But we define this
+ * to map all unknown chars to a single unknown glyph to
+ * reduce pressure on cache. */
+
+ if (likely (unicode < ARRAY_LENGTH (_cairo_twin_charmap)))
+ *glyph = unicode;
+ else
+ *glyph = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+/*
+ * Face constructor
+ */
+
+static cairo_font_face_t *
+_cairo_font_face_twin_create_internal (void)
+{
+ cairo_font_face_t *twin_font_face;
+
+ twin_font_face = cairo_user_font_face_create ();
+ cairo_user_font_face_set_init_func (twin_font_face, twin_scaled_font_init);
+ cairo_user_font_face_set_render_glyph_func (twin_font_face, twin_scaled_font_render_glyph);
+ cairo_user_font_face_set_unicode_to_glyph_func (twin_font_face, twin_scaled_font_unicode_to_glyph);
+
+ return twin_font_face;
+}
+
+cairo_font_face_t *
+_cairo_font_face_twin_create_fallback (void)
+{
+ cairo_font_face_t *twin_font_face;
+
+ twin_font_face = _cairo_font_face_twin_create_internal ();
+ if (! twin_font_face_create_properties (twin_font_face)) {
+ cairo_font_face_destroy (twin_font_face);
+ return (cairo_font_face_t *) &_cairo_font_face_nil;
+ }
+
+ return twin_font_face;
+}
+
+cairo_status_t
+_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face,
+ cairo_font_face_t **font_face)
+{
+ cairo_status_t status;
+ cairo_font_face_t *twin_font_face;
+
+ twin_font_face = _cairo_font_face_twin_create_internal ();
+ status = twin_font_face_set_properties_from_toy (twin_font_face, toy_face);
+ if (status) {
+ cairo_font_face_destroy (twin_font_face);
+ return status;
+ }
+
+ *font_face = twin_font_face;
+
+ return CAIRO_STATUS_SUCCESS;
+}