/*
* Copyright (C) 1997 and 1998 WIDE Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* $Id: tfont.c,v 1.44 2003/09/12 02:04:55 nishida Exp $
*/
#include "mgp.h"
#ifdef FREETYPE
int tfcachesize = 3000; /*XXX*/
#define TFFONT_NUM 128
#define TFHASH_SIZE 255
#define TFCACHE_HASH(c, siz) ((c ^ siz) % TFHASH_SIZE)
static TT_Raster_Map bitmap;
static TT_Face_Properties properties[TFFONT_NUM];
static TT_Face face[TFFONT_NUM];
static TT_Glyph glyph[TFFONT_NUM];
static TT_Instance instance[TFFONT_NUM];
static TT_Glyph_Metrics metrics;
static TT_Error error;
static TT_Engine engine;
static XImage *xim;
static char *tf_curname[4]; /* contains iso8859-[2-4] fonts */
static char *tf_mcurname = NULL;
static int tfcinitdone;
static int tfcachecnt;
static struct tfont tfclru;
static struct tfont tfcache[TFHASH_SIZE];
static int tffontcnt = 0;
static int tfcachehit = 0;
static int tfcachemiss = 0;
static int tfcuridx = -1;
static char tfloadedfont[1024][TFFONT_NUM];
static int tfloadedregistry[TFFONT_NUM];
#define TFC_INSHASH(tfc) { \
struct tfont *h, *n; \
h = &tfcache[TFCACHE_HASH(tfc->code, tfc->size)]; \
n = h->next; tfc->next = n; n->prev = tfc; \
h->next = tfc; tfc->prev = h; \
}
#define TFC_DELHASH(tfc) { \
struct tfont *n, *p; \
n = tfc->next; p = tfc->prev; \
n->prev = p; p->next = n; \
}
#define TFC_INSLRU(tfc) { \
struct tfont *p; \
p = tfclru.lruprev; tfc->lruprev = p; p->lrunext = tfc; \
tfclru.lruprev = tfc; tfc->lrunext = &tfclru; \
}
#define TFC_DELLRU(tfc) { \
struct tfont *n, *p; \
n = tfc->lrunext; p = tfc->lruprev; \
n->lruprev = p; p->lrunext = n; \
}
static void tfc_init __P((void));
static void tfc_free __P((struct tfont *));
static struct tfont *tfc_lookup __P((u_int, u_int, char*, int));
static struct tfont *tfc_alloc __P((u_int, u_int, char *, char *));
static short CharToUnicode __P((u_int, char *));
static short jistosjis __P((u_int));
static TT_Error LoadTrueTypeChar __P((int, int));
#define TTFLOOR(x) ((x) & -64)
#define TTCEIL(x) (((x) + 63) & -64)
static void
tfc_init()
{
u_int i;
struct tfont *tfc;
/* Initialize engine */
if ((error = TT_Init_FreeType(&engine))) {
fprintf(stderr, "Error while initializing engine, code = %d.\n",
(int)error);
cleanup(-1);
}
tfc = tfcache;
for (tfc = tfcache, i = 0; i < TFHASH_SIZE; tfc++, i++)
tfc->next = tfc->prev = tfc;
tfclru.lrunext = tfclru.lruprev = &tfclru;
tfcinitdone ++;
latin_unicode_map_init();
#ifdef FREETYPE_CHARSET16
unicode_map_init();
#endif
}
static void
tfc_free(tfc)
struct tfont *tfc;
{
TFC_DELHASH(tfc);
TFC_DELLRU(tfc);
free(tfc->fontname);
free(tfc->dbitmap);
free(tfc);
tfcachecnt--;
}
int
tfc_setsize(char_size)
u_int char_size;
{
if (!tfcinitdone)
return -1;
if (tfcuridx < 0 || tffontcnt <= tfcuridx) {
if (verbose)
fprintf(stderr, "tfc_setsize: font index out of bound\n");
return -1;
}
error = TT_Set_Instance_CharSize(instance[tfcuridx],
(char_size * 0.75) * 64);
if (error) {
fprintf(stderr,
"Could not set device resolutions for \"%s\".\n",
tfloadedfont[tfcuridx]);
return -1;
}
return 0;
}
struct tfont *
tfc_get(code, size, force, registry, charset16)
u_int code, size;
int force;
char *registry;
int charset16;
{
struct tfont *tfc, *ntfc;
int regid;
if (!tfcinitdone)
tfc_init();
if (charset16)
tfc = tfc_lookup(code, size, tf_mcurname, 0);
else {
if (code >= 0xa0 && ((!registry || !registry[0]) && mgp_charset)){
regid = get_regid(mgp_charset);
} else
regid = get_regid(registry);
tfc = tfc_lookup(code, size, tf_curname[regid], regid);
}
if (tfc == NULL) {
/* XXX */
if (tfcachecnt >= tfcachesize) {
if (!force)
return NULL;
tfc = tfclru.lrunext;
while (tfcachecnt >= tfcachesize) {
if (tfc == &tfclru)
break;
ntfc = tfc->lrunext;
if (tfc->ref == 0)
tfc_free(tfc);
tfc = ntfc;
}
}
if (charset16)
tfc = tfc_alloc(code, size, tf_mcurname, registry);
else
tfc = tfc_alloc(code, size, tf_curname[regid], registry);
}
return tfc;
}
void
tfc_setfont(name, charset16, registry)
char *name;
int charset16; /*2-octet charset?*/
char *registry;
{
char *fontname = NULL;
u_int i, regid = 0;
int res = 96; /* XXX */
char pathname[MAXPATHLEN];
int trial;
TT_Face tface;
if (!tfcinitdone)
tfc_init();
if (TFFONT_NUM <= tffontcnt) {
fprintf(stderr, "internal error: "
"too many fonts opened (increase TFFONT_NUM)\n");
cleanup(-1);
}
if (!name || !name[0]) {
if (freetypefont0 && freetypefont0[0]) {
if (verbose) {
fprintf(stderr, "tfc_setfont: "
"font name not given, "
"using last resort (%s)\n",
freetypefont0);
}
name = freetypefont0;
} else {
if (verbose) {
fprintf(stderr, "tfc_setfont: "
"font name not given, fail\n");
}
goto fail;
}
}
/* check font cache first */
for (trial = 0; trial < 2; trial++) {
switch (trial) {
case 0:
fontname = name;
break;
case 1:
snprintf(pathname, sizeof(pathname),
"%s/%s", freetypefontdir, name);
fontname = pathname;
break;
}
for (i = 0; i < tffontcnt; i ++) {
if (!strcmp(fontname, tfloadedfont[i])) {
/* already loaded */
#if 0
if (verbose) {
fprintf(stderr,
"font cache hit for \"%s\"\n",
fontname);
}
#endif
tfcuridx = i;
if (charset16)
tf_mcurname = tfloadedfont[i];
else {
regid = get_regid(registry);
tf_curname[regid] = tfloadedfont[i];
}
#if 0
tfc_setsize(char_size);
#endif
return;
}
}
}
/* try to load font */
for (trial = 0; trial < 3; trial++) {
switch (trial) {
case 0:
fontname = name;
break;
case 1:
snprintf(pathname, sizeof(pathname),
"%s/%s", freetypefontdir, name);
fontname = pathname;
break;
case 2:
if (!freetypefont0 || !freetypefont0[0])
continue;
fontname = freetypefont0;
/* already loaded? */
for (i = 0; i < tffontcnt; i ++) {
if (!strcmp(fontname, tfloadedfont[i])){
tfcuridx = i;
if (verbose)
fprintf(stderr, "font \"%s\" already loaded: %d\n",
fontname, tffontcnt);
goto cached;
}
}
break;
}
/* Load face */
if (verbose) fprintf(stderr, "trying to open font \"%s\"\n", fontname);
error = TT_Open_Face(engine, fontname, &tface);
if (!error)
break;
}
if (trial == 2) {
if (verbose) {
fprintf(stderr, "font \"%s\" not found, "
"last resort font \"%s\" was used\n",
name, freetypefont0);
}
} else if (trial == 3) {
if (verbose) {
fprintf(stderr, "could not load font \"%s\", "
"using other rendering engine\n", name);
}
goto fail;
}
if (verbose) {
fprintf(stderr, "font \"%s\" opened successfully\n",
fontname);
}
/* get registry id */
if ((regid = get_regid(registry)) < 0){
fprintf(stderr, "font \"%s\" has irregal registry\n", fontname);
goto fail2;
}
tfloadedregistry[tffontcnt] = regid;
strcpy(tfloadedfont[tffontcnt], fontname);
tfcuridx = tffontcnt;
face[tfcuridx] = tface;
tffontcnt++;
/* get face properties and allocate preload arrays */
error = TT_Get_Face_Properties(face[tfcuridx], &properties[tfcuridx]);
if (error) {
fprintf(stderr, "Could not create face property for "
"\"%s\" (err 0x%04x).\n", fontname, (int)error);
goto fail2;
}
/* create glyph */
error = TT_New_Glyph(face[tfcuridx], &glyph[tfcuridx]);
if (error) {
fprintf(stderr, "Could not create glyph container for "
"\"%s\" (err 0x%04x).\n", fontname, (int)error);
goto fail2;
}
/* create instance */
error = TT_New_Instance(face[tfcuridx], &instance[tfcuridx]);
if (error) {
fprintf(stderr, "Could not create instance for "
"\"%s\" (err 0x%04x).\n", fontname, (int)error);
goto fail2;
}
error = TT_Set_Instance_Resolutions(instance[tfcuridx], res, res);
if (error) {
fprintf(stderr, "Could not set device resolutions for "
"\"%s\" (err 0x%04x).\n", fontname, (int)error);
goto fail2;
}
cached:
if (charset16)
tf_mcurname = tfloadedfont[tfcuridx];
else {
if (registry)
tf_curname[regid] = tfloadedfont[tfcuridx];
else {
/* this should be default font */
for (i = 0; i < 4; i ++)
tf_curname[i] = tfloadedfont[tfcuridx];
}
}
if (tfc_setsize(char_size[caching]) < 0)
goto fail2;
/* success */
return;
fail2:
TT_Close_Face(tface);
tffontcnt--;
fail:
if (!charset16){
tf_curname[regid] = "";
tfcuridx = -1;
} else
tf_mcurname = "";
return;
}
static struct tfont *
tfc_lookup(code, size, fontname, regid)
u_int code, size;
char *fontname;
int regid;
{
u_int i;
struct tfont *tfc, *htfc;
if (!fontname)
return NULL;
for (i = 0; i < tffontcnt; i ++) {
if (!strcmp(fontname, tfloadedfont[i]))
tfcuridx = i;
}
htfc = &tfcache[TFCACHE_HASH(code, size)];
for (tfc = htfc->next; tfc != htfc; tfc = tfc->next) {
if (tfc->code == code
&& tfc->size == size
&& tfc->regid == regid
&& strcmp(tfc->fontname, fontname) == 0) {
tfcachehit++;
TFC_DELLRU(tfc);
TFC_INSLRU(tfc);
return tfc;
}
}
tfcachemiss++;
return NULL;
}
static struct tfont *
tfc_alloc(code, size, fontname, registry)
u_int code, size;
char *fontname;
char *registry;
{
struct tfont *tfc;
short unicode;
/* if no font was ever loaded, try loading the last resort font */
if (!tffontcnt) {
if (verbose)
fprintf(stderr, "no fontcount\n");
tfc_setfont(NULL, 0, NULL);
}
/* font out of bounds, suggests no font was available */
if (tfcuridx < 0 || tffontcnt <= tfcuridx) {
if (verbose)
fprintf(stderr, "tfc_alloc: font index out of bound\n");
return NULL;
}
if (fontname == NULL)
return NULL;
/* This is required! */
if (tfc_setsize(char_size[caching]) < 0)
return NULL;
tfc = (struct tfont *)malloc(sizeof(*tfc));
if (tfc == NULL) {
fprintf(stderr, "tfc_alloc: malloc failed\n");
return NULL;
}
#if 1
unicode = CharToUnicode(code, registry);
#else
unicode = code;
#endif
if (!unicode)
return NULL;
if ((error = LoadTrueTypeChar(unicode, 0))) {
fprintf(stderr, "LoadTrueTypeChar fail %d\n", (int)error);
fprintf(stderr, "fontfile: \"%s\" char: '%c' (0x%04x)\n",
tfloadedfont[tfcuridx],
isprint(unicode) ? unicode : '?',
unicode);
return NULL;
}
TT_Get_Glyph_Metrics(glyph[tfcuridx], &metrics);
/*
* TT_Get_Glyph_Pixmap generates pixmap starting from FreeType
* origin (leftmost side, baseline). Because of this 3rd and 4th
* arguments are necessary.
* For X axis (3rd argument), we have to take metrics.bearingX as
* offset. Y axis must be shifted if there's descent box (image
* below the baseline). 4th argument is specified for that.
*/
bitmap.rows = (metrics.bbox.yMax - metrics.bbox.yMin) >> 6;
bitmap.rows += 2; /* insurance to cope with number-round error */
bitmap.width = metrics.bbox.xMax - metrics.bbox.xMin;
bitmap.width >>= 6;
bitmap.width += 2; /* insurance to cope with number-round error */
bitmap.cols = (bitmap.width+3) & -4;
bitmap.flow = TT_Flow_Down;
bitmap.size = (long)bitmap.cols * bitmap.rows;
bitmap.bitmap = (void *)malloc((int)bitmap.size);
memset(bitmap.bitmap, 0, bitmap.size);
/* be very careful about CEIL/FLOOR. */
TT_Get_Glyph_Pixmap(glyph[tfcuridx], &bitmap,
TTCEIL(-metrics.bearingX),
TTCEIL(metrics.bbox.yMax - metrics.bbox.yMin - metrics.bearingY));
tfc->code = code;
tfc->size = size;
tfc->width = bitmap.width;
tfc->bwidth = bitmap.cols;
tfc->height = bitmap.rows;
/*
* ascent must be derived from descent, to avoid number-rounding
* errors.
*/
tfc->descent =
TTCEIL(metrics.bbox.yMax - metrics.bbox.yMin - metrics.bearingY) >> 6;
tfc->ascent = bitmap.rows - tfc->descent;
tfc->xoff = metrics.bearingX >> 6;
tfc->fontname = strdup(fontname);
tfc->charlen = metrics.advance >> 6;
tfc->xmax = metrics.bbox.xMax >> 6;
tfc->ref = 0;
tfc->dbitmap = bitmap.bitmap;
tfc->regid = get_regid(registry);
if (!tfc->charlen) tfc->charlen = 1;
TFC_INSHASH(tfc);
TFC_INSLRU(tfc);
tfcachecnt++;
return tfc;
}
static short
CharToUnicode(code, registry)
u_int code;
char *registry;
{
TT_CharMap char_map;
unsigned short i, n;
unsigned short platform, encoding;
unsigned int regid;
char p;
/* First, look for a Unicode charmap */
#ifdef HAVE_TT_FACE_PROPERTIES_CHARMAPS
n = properties[tfcuridx].num_CharMaps;
#else
n = TT_Get_CharMap_Count(face[tfcuridx]);
#endif
#if 1
if (verbose) {
fprintf(stderr, "%d charmaps for font %s code %04x\n",
n, tfloadedfont[tfcuridx], code & 0xffff);
}
#endif
for (i = 0; i < n; i++) {
TT_Get_CharMap_ID(face[tfcuridx], i, &platform, &encoding);
if ((platform == 3 && encoding == 1)
|| (platform == 0 && encoding == 0)) {
TT_Get_CharMap(face[tfcuridx], i, &char_map);
#ifdef FREETYPE_CHARSET16
if (code > 256 && strncmp(registry, "jisx0208.1983-", 14) != 0) {
/*
* unicode_map assumes JIS X0208.
* it is not usable for other encodings.
* try the next CharMap available.
*/
continue;
}
if (code > 256)
code = unicode_map[code];
#endif /* FREETYPE_CHARSET16 */
#if 1 /* latin2-4 encoding processing */
if (code > 0xa0 && code < 256) {
if (registry)
regid = get_regid(registry) -1;
else {
if (mgp_charset)
regid = get_regid(mgp_charset) -1;
}
if (latin_unicode_map[regid][code])
code = latin_unicode_map[regid][code];
}
#endif
/*
* For freetype 1.4, suggested by mituharu@math.s.chiba-u.ac.jp
*/
#if TT_FREETYPE_MAJOR == 1 && TT_FREETYPE_MINOR == 4
return TT_Char_Index(char_map, (u_short) code);
#else
return TT_Char_Index(char_map, (short) code);
#endif
}
#ifdef FREETYPE_CHARSET16
else {
/*
* very adhoc processing for truetype fonts generated by cb2ttj.
*/
if ((platform == 3 && encoding == 2)
|| (platform == 1 && encoding == 1)) {
if (code < 256) continue;
TT_Get_CharMap(face[tfcuridx], i, &char_map);
return TT_Char_Index(char_map, (u_short) jistosjis(code));
} else {
if (platform == 1 && encoding == 0) {
if (code >= 0xa0) continue;
TT_Get_CharMap(face[tfcuridx], i, &char_map);
return TT_Char_Index(char_map, (short) code);
}
#endif
else {
/* symbol font */
if (platform == 3 && encoding == 0) {
code |= 0xf000;
TT_Get_CharMap(face[tfcuridx],
i, &char_map);
return TT_Char_Index(char_map,
code);
}
}
#ifdef FREETYPE_CHARSET16
}
}
#endif
}
fprintf(stderr, "Sorry, but the fontfile \"%s\" doesn't "
"contain any Unicode mapping table\n", tfloadedfont[tfcuridx]);
return 0;
}
static TT_Error
LoadTrueTypeChar(int idx, int hint)
{
int flags;
flags = TTLOAD_SCALE_GLYPH;
if (hint)
flags |= TTLOAD_HINT_GLYPH;
return TT_Load_Glyph(instance[tfcuridx], glyph[tfcuridx], idx, flags);
}
/*
* NOTE: this is upper-layer's responsibility to adjust the (bx, by)
* so that the font bitmap fits into the given XImage.
* see draw_onechar_tf() for the code.
*/
XImage *
tfc_image(tfc, fore, back, xim, bx, by)
struct tfont *tfc;
long fore, back;
XImage *xim;
int bx, by;
{
int x, y;
static XColor col[5];
u_char d, *s;
u_long p;
XColor *bcol = NULL, *bc;
u_int charlen;
charlen = tfc->charlen;
if (back == -1) {
u_long r, g, b;
int dots;
dots = charlen * tfc->height;
bcol = malloc(sizeof(XColor) * dots);
if (bcol == NULL)
return NULL;
bc = bcol;
for (y = 0; y < tfc->height; y++) {
for (x = 0; x < charlen; x++, bc++) {
bc->pixel = XGetPixel(xim, bx + tfc->xoff + x,
by - tfc->ascent + y);
bc->flags = DoRed|DoGreen|DoBlue;
}
}
XQueryColors(display, colormap, bcol, dots);
r = g = b = 0;
for (y = 0; y < tfc->height; y++) {
for (x = 0; x < charlen; x++) {
r += bcol[x].red;
g += bcol[x].green;
b += bcol[x].blue;
}
}
r /= dots; g /= dots; b /= dots;
bc = &col[0];
if (bc->red == r && bc->green == g && bc->blue == b)
bc->pixel = back;
else {
bc->pixel = ~back;
bc->red = r; bc->green = g; bc->blue = b;
}
}
if (fore != col[4].pixel || back != col[0].pixel) {
col[4].pixel = fore;
col[4].flags = DoRed|DoGreen|DoBlue;
if (back != -1) {
col[3].pixel = back;
col[3].flags = DoRed|DoGreen|DoBlue;
XQueryColors(display, colormap, &col[3], 2);
col[0] = col[3];
} else {
XQueryColor(display, colormap, &col[4]);
}
for (x = 3; x > 0; x--) {
col[x].red = (col[4].red *x + col[0].red *(4-x)) /4;
col[x].green = (col[4].green*x + col[0].green*(4-x)) /4;
col[x].blue = (col[4].blue *x + col[0].blue *(4-x)) /4;
if (!XAllocColor(display, colormap, &col[x])) {
if (verbose)
printf("tfc_image: cannot allocate color for level %d (using %d)\n", x, x + 1);
col[x].pixel = col[x + 1].pixel;
}
else regist_alloc_colors(&font_clr, &col[x].pixel, 1);
}
}
/* XXX: need optimization */
s = tfc->dbitmap;
bc = bcol;
for (y = 0; y < tfc->height; y++) {
for (x = 0; x < tfc->bwidth; x++) {
d = *s++;
if (d && x < tfc->width) {
p = col[d].pixel;
XPutPixel(xim, bx + tfc->xoff + x,
by - tfc->ascent + y, p);
}
}
}
if (mgp_flag & FL_GLYPHEDGE) {
/*
* for debugging treatment of font metrics, for 16bpp displays
*/
/* pixmap bounding box */
for (y = 0; y < tfc->height; y++) {
XPutPixel(xim, bx + tfc->xoff, by - tfc->ascent + y,
0xffff);
XPutPixel(xim, bx + tfc->xoff + tfc->width - 1,
by - tfc->ascent + y, 0xffff);
}
for (x = 0; x < tfc->width; x++) {
XPutPixel(xim, bx + tfc->xoff + x, by - tfc->ascent,
0xffff);
XPutPixel(xim, bx + tfc->xoff + x,
by - tfc->ascent + tfc->height - 1, 0xffff);
}
/* origin */
XPutPixel(xim, bx, by, 0xaaaa);
/* baseline */
for (x = 0; x < tfc->width; x++)
XPutPixel(xim, bx + tfc->xoff + x, by, 0xaaaa);
}
if (bcol)
free(bcol);
return xim;
}
static short
jistosjis(code)
u_int code;
{
u_char c1 = code >> 8;
u_char c2 = code % 256;
int rowOffset = c1 < 95 ? 112: 176;
int cellOffset = c1 % 2 ? (c2 > 95 ? 32 : 31) : 126;
return (((c1 + 1) >> 1) + rowOffset) * 256 + c2 + cellOffset;
}
#endif /* FRRETYPE */
syntax highlighted by Code2HTML, v. 0.9.1