/* * 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., 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 Keith Packard * * Contributor(s): * Keith Packard * Behdad Esfahbod */ #include "cairoint.h" #include /* * 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; iNAME)) \ 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 cairo_status_t twin_font_face_create_properties (cairo_font_face_t *twin_face, twin_face_properties_t **props_out) { twin_face_properties_t *props; cairo_status_t status; props = malloc (sizeof (twin_face_properties_t)); if (unlikely (props == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); props->stretch = TWIN_STRETCH_NORMAL; props->monospace = FALSE; props->smallcaps = FALSE; status = cairo_font_face_set_user_data (twin_face, &twin_properties_key, props, free); if (unlikely (status)) { free (props); return status; } if (props_out) *props_out = props; return CAIRO_STATUS_SUCCESS; } static cairo_status_t twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face, cairo_toy_font_face_t *toy_face) { cairo_status_t status; twin_face_properties_t *props; status = twin_font_face_create_properties (twin_face, &props); if (unlikely (status)) return status; 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; cairo_status_t status; twin_font_face = _cairo_font_face_twin_create_internal (); status = twin_font_face_create_properties (twin_font_face, NULL); if (status) { 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; }