forked from KolibriOS/kolibrios
753 lines
18 KiB
C
753 lines
18 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|