/* * 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 */