/* * $XFree86: xc/lib/Xft/xftfreetype.c,v 1.14 2001/09/21 19:54:53 keithp Exp $ * * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "xftint.h" FT_Library _XftFTlibrary; typedef struct _XftFtEncoding { const char *name; FT_Encoding encoding; } XftFtEncoding; static XftFtEncoding xftFtEncoding[] = { { "iso10646-1", ft_encoding_unicode, }, { "iso8859-1", ft_encoding_unicode, }, { "apple-roman", ft_encoding_apple_roman }, { "adobe-fontspecific", ft_encoding_symbol, }, { "glyphs-fontspecific",ft_encoding_none, }, }; #define NUM_FT_ENCODINGS (sizeof xftFtEncoding / sizeof xftFtEncoding[0]) #define FT_Matrix_Equal(a,b) ((a)->xx == (b)->xx && \ (a)->yy == (b)->yy && \ (a)->xy == (b)->xy && \ (a)->yx == (b)->yx) XftPattern * XftFreeTypeQuery (const char *file, int id, int *count) { FT_Face face; XftPattern *pat; int slant; int weight; int i, j; if (FT_New_Face (_XftFTlibrary, file, id, &face)) return 0; *count = face->num_faces; pat = XftPatternCreate (); if (!pat) goto bail0; if (!XftPatternAddBool (pat, XFT_CORE, False)) goto bail1; if (!XftPatternAddBool (pat, XFT_OUTLINE, (face->face_flags & FT_FACE_FLAG_SCALABLE) != 0)) goto bail1; if (!XftPatternAddBool (pat, XFT_SCALABLE, (face->face_flags & FT_FACE_FLAG_SCALABLE) != 0)) goto bail1; slant = XFT_SLANT_ROMAN; if (face->style_flags & FT_STYLE_FLAG_ITALIC) slant = XFT_SLANT_ITALIC; if (!XftPatternAddInteger (pat, XFT_SLANT, slant)) goto bail1; weight = XFT_WEIGHT_MEDIUM; if (face->style_flags & FT_STYLE_FLAG_BOLD) weight = XFT_WEIGHT_BOLD; if (!XftPatternAddInteger (pat, XFT_WEIGHT, weight)) goto bail1; if (!XftPatternAddString (pat, XFT_FAMILY, face->family_name)) goto bail1; if (!XftPatternAddString (pat, XFT_STYLE, face->style_name)) goto bail1; if (!XftPatternAddString (pat, XFT_FILE, file)) goto bail1; if (!XftPatternAddInteger (pat, XFT_INDEX, id)) goto bail1; #if 0 if ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0) if (!XftPatternAddInteger (pat, XFT_SPACING, XFT_MONO)) goto bail1; #endif if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) { for (i = 0; i < face->num_fixed_sizes; i++) if (!XftPatternAddDouble (pat, XFT_PIXEL_SIZE, (double) face->available_sizes[i].height)) goto bail1; } for (i = 0; i < face->num_charmaps; i++) { #if 0 printf ("face %s encoding %d %c%c%c%c\n", face->family_name, i, face->charmaps[i]->encoding >> 24, face->charmaps[i]->encoding >> 16, face->charmaps[i]->encoding >> 8, face->charmaps[i]->encoding >> 0); #endif for (j = 0; j < NUM_FT_ENCODINGS; j++) { if (face->charmaps[i]->encoding == xftFtEncoding[j].encoding) { if (!XftPatternAddString (pat, XFT_ENCODING, xftFtEncoding[j].name)) goto bail1; } } } if (!XftPatternAddString (pat, XFT_ENCODING, "glyphs-fontspecific")) goto bail1; FT_Done_Face (face); return pat; bail1: XftPatternDestroy (pat); bail0: FT_Done_Face (face); return 0; } /* * List of all open files (each face in a file is managed separately) */ typedef struct _XftFtFile { struct _XftFtFile *next; int ref; char *file; int id; FT_Face face; FT_F26Dot6 size; FT_Matrix matrix; int charmap; } XftFtFile; static XftFtFile *_XftFtFiles; static XftFtFile * _XftFreeTypeOpenFile (char *file, int id) { XftFtFile *f; FT_Face face; for (f = _XftFtFiles; f; f = f->next) { if (!strcmp (f->file, file) && f->id == id) { ++f->ref; if (_XftFontDebug () & XFT_DBG_REF) printf ("FontFile %s/%d matches existing (%d)\n", file, id, f->ref); return f; } } if (FT_New_Face (_XftFTlibrary, file, id, &face)) return 0; f = malloc (sizeof (XftFtFile) + strlen (file) + 1); if (!f) return 0; if (_XftFontDebug () & XFT_DBG_REF) printf ("FontFile %s/%d matches new\n", file, id); f->next = _XftFtFiles; _XftFtFiles = f; f->ref = 1; f->file = (char *) (f+1); strcpy (f->file, file); f->id = id; f->face = face; f->size = 0; f->charmap = -1; return f; } Bool XftFreeTypeSetFace (FT_Face face, FT_F26Dot6 size, int charmap, FT_Matrix *matrix) { XftFtFile *f, **prev; for (prev = &_XftFtFiles; (f = *prev); prev = &f->next) { if (f->face == face) { /* LRU */ if (prev != &_XftFtFiles) { *prev = f->next; f->next = _XftFtFiles; _XftFtFiles = f; } if (f->size != size) { if (_XftFontDebug() & XFT_DBG_GLYPH) printf ("Set face size to %d (%d)\n", (int) (size >> 6), (int) size); if (FT_Set_Char_Size (face, size, size, 0, 0)) return False; f->size = size; } if (f->charmap != charmap && charmap != -1) { if (_XftFontDebug() & XFT_DBG_GLYPH) printf ("Set face charmap to %d\n", charmap); if (FT_Set_Charmap (face, face->charmaps[charmap])) return False; f->charmap = charmap; } if (!FT_Matrix_Equal (&f->matrix, matrix)) { if (_XftFontDebug() & XFT_DBG_GLYPH) printf ("Set face matrix to (%g,%g,%g,%g)\n", (double) matrix->xx / 0x10000, (double) matrix->xy / 0x10000, (double) matrix->yx / 0x10000, (double) matrix->yy / 0x10000); FT_Set_Transform (face, matrix, 0); f->matrix = *matrix; } break; } } return True; } static void _XftFreeTypeCloseFile (XftFtFile *f) { XftFtFile **prev; if (--f->ref != 0) return; for (prev = &_XftFtFiles; *prev; prev = &(*prev)->next) { if (*prev == f) { *prev = f->next; break; } } FT_Done_Face (f->face); free (f); } /* * Cache of all glyphsets */ typedef struct _XftFtGlyphSet { struct _XftFtGlyphSet *next; int ref; XftFtFile *file; Bool minspace; int char_width; XftFontStruct font; } XftFtGlyphSet; static XftFtGlyphSet *_XftFtGlyphSets; XftFontStruct * XftFreeTypeOpen (Display *dpy, XftPattern *pattern) { XftFtFile *file; FT_Face face; XftFtGlyphSet *gs; char *filename; int id; double dsize; FT_F26Dot6 size; int rgba; int spacing; int char_width; Bool antialias; Bool minspace; char *encoding_name; XftFontStruct *font; int j; FT_Encoding encoding; int charmap; FT_Matrix matrix; XftMatrix *font_matrix; int extra; int height, ascent, descent; XRenderPictFormat pf, *format; /* * Open the file */ if (XftPatternGetString (pattern, XFT_FILE, 0, &filename) != XftResultMatch) goto bail0; if (XftPatternGetInteger (pattern, XFT_INDEX, 0, &id) != XftResultMatch) goto bail0; file = _XftFreeTypeOpenFile (filename, id); if (!file) goto bail0; face = file->face; /* * Extract the glyphset information from the pattern */ if (XftPatternGetString (pattern, XFT_ENCODING, 0, &encoding_name) != XftResultMatch) goto bail0; if (XftPatternGetDouble (pattern, XFT_PIXEL_SIZE, 0, &dsize) != XftResultMatch) goto bail0; switch (XftPatternGetInteger (pattern, XFT_RGBA, 0, &rgba)) { case XftResultNoMatch: rgba = XFT_RGBA_NONE; break; case XftResultMatch: break; default: goto bail0; } switch (XftPatternGetBool (pattern, XFT_ANTIALIAS, 0, &antialias)) { case XftResultNoMatch: antialias = True; break; case XftResultMatch: break; default: goto bail0; } switch (XftPatternGetBool (pattern, XFT_MINSPACE, 0, &minspace)) { case XftResultNoMatch: minspace = False; break; case XftResultMatch: break; default: goto bail0; } switch (XftPatternGetInteger (pattern, XFT_SPACING, 0, &spacing)) { case XftResultNoMatch: spacing = XFT_PROPORTIONAL; break; case XftResultMatch: break; default: goto bail1; } if (XftPatternGetInteger (pattern, XFT_CHAR_WIDTH, 0, &char_width) != XftResultMatch) { char_width = 0; } else if (char_width) spacing = XFT_MONO; matrix.xx = matrix.yy = 0x10000; matrix.xy = matrix.yx = 0; switch (XftPatternGetMatrix (pattern, XFT_MATRIX, 0, &font_matrix)) { case XftResultNoMatch: break; case XftResultMatch: matrix.xx = 0x10000L * font_matrix->xx; matrix.yy = 0x10000L * font_matrix->yy; matrix.xy = 0x10000L * font_matrix->xy; matrix.yx = 0x10000L * font_matrix->yx; break; default: goto bail1; } if (XftPatternGetInteger (pattern, XFT_CHAR_WIDTH, 0, &char_width) != XftResultMatch) { char_width = 0; } else if (char_width) spacing = XFT_MONO; encoding = face->charmaps[0]->encoding; for (j = 0; j < NUM_FT_ENCODINGS; j++) if (!strcmp (encoding_name, xftFtEncoding[j].name)) { encoding = xftFtEncoding[j].encoding; break; } size = (FT_F26Dot6) (dsize * 64.0); if (encoding == ft_encoding_none) charmap = -1; else { for (charmap = 0; charmap < face->num_charmaps; charmap++) if (face->charmaps[charmap]->encoding == encoding) break; if (charmap == face->num_charmaps) goto bail1; } /* * Match an existing glyphset */ for (gs = _XftFtGlyphSets; gs; gs = gs->next) { if (gs->file == file && gs->minspace == minspace && gs->char_width == char_width && gs->font.size == size && gs->font.spacing == spacing && gs->font.charmap == charmap && gs->font.rgba == rgba && gs->font.antialias == antialias && FT_Matrix_Equal (&gs->font.matrix, &matrix)) { ++gs->ref; if (_XftFontDebug () & XFT_DBG_REF) { printf ("Face size %g matches existing (%d)\n", dsize, gs->ref); } return &gs->font; } } if (_XftFontDebug () & XFT_DBG_REF) { printf ("Face size %g matches new\n", dsize); } /* * No existing glyphset, create another */ gs = malloc (sizeof (XftFtGlyphSet)); if (!gs) goto bail1; gs->ref = 1; gs->file = file; gs->minspace = minspace; gs->char_width = char_width; font = &gs->font; if (antialias) { if (rgba) { pf.depth = 32; pf.type = PictTypeDirect; pf.direct.alpha = 24; pf.direct.alphaMask = 0xff; pf.direct.red = 16; pf.direct.redMask = 0xff; pf.direct.green = 8; pf.direct.greenMask = 0xff; pf.direct.blue = 0; pf.direct.blueMask = 0xff; format = XRenderFindFormat(dpy, PictFormatType| PictFormatDepth| PictFormatAlpha| PictFormatAlphaMask| PictFormatRed| PictFormatRedMask| PictFormatGreen| PictFormatGreenMask| PictFormatBlue| PictFormatBlueMask, &pf, 0); } else { pf.depth = 8; pf.type = PictTypeDirect; pf.direct.alpha = 0; pf.direct.alphaMask = 0xff; format = XRenderFindFormat(dpy, PictFormatType| PictFormatDepth| PictFormatAlpha| PictFormatAlphaMask, &pf, 0); } } else { pf.depth = 1; pf.type = PictTypeDirect; pf.direct.alpha = 0; pf.direct.alphaMask = 0x1; format = XRenderFindFormat(dpy, PictFormatType| PictFormatDepth| PictFormatAlpha| PictFormatAlphaMask, &pf, 0); } if (!format) goto bail2; if (!XftFreeTypeSetFace (face, size, charmap, &matrix)) goto bail2; descent = -(face->size->metrics.descender >> 6); ascent = face->size->metrics.ascender >> 6; if (minspace) { height = ascent + descent; } else { height = face->size->metrics.height >> 6; #if 0 extra = (height - (ascent + descent)); if (extra > 0) { ascent = ascent + extra / 2; descent = height - ascent; } else if (extra < 0) height = ascent + descent; #endif } font->ascent = ascent; font->descent = descent; font->height = height; if (char_width) font->max_advance_width = char_width; else font->max_advance_width = face->size->metrics.max_advance >> 6; gs->next = _XftFtGlyphSets; _XftFtGlyphSets = gs; font->glyphset = XRenderCreateGlyphSet (dpy, format); font->size = size; font->spacing = spacing; font->format = format; font->realized =0; font->nrealized = 0; font->rgba = rgba; font->antialias = antialias; font->charmap = charmap; font->transform = (matrix.xx != 0x10000 || matrix.xy != 0 || matrix.yx != 0 || matrix.yy != 0x10000); font->matrix = matrix; font->face = face; return font; bail2: free (gs); bail1: _XftFreeTypeCloseFile (file); bail0: return 0; } void XftFreeTypeClose (Display *dpy, XftFontStruct *font) { XftFtGlyphSet *gs, **prev; for (prev = &_XftFtGlyphSets; (gs = *prev); prev = &gs->next) { if (&gs->font == font) { if (--gs->ref == 0) { XRenderFreeGlyphSet (dpy, font->glyphset); if (font->realized) free (font->realized); _XftFreeTypeCloseFile (gs->file); *prev = gs->next; free (gs); } break; } } } XftFontStruct * XftFreeTypeGet (XftFont *font) { if (font->core) return 0; return font->u.ft.font; } /* #define XFT_DEBUG_FONTSET */ Bool XftInitFtLibrary (void) { char **d; char *cache; if (_XftFTlibrary) return True; if (FT_Init_FreeType (&_XftFTlibrary)) return False; _XftFontSet = XftFontSetCreate (); if (!_XftFontSet) return False; cache = XftConfigGetCache (); if (cache) XftFileCacheLoad (cache); for (d = XftConfigDirs; d && *d; d++) { #ifdef XFT_DEBUG_FONTSET printf ("scan dir %s\n", *d); #endif XftDirScan (_XftFontSet, *d, False); } #ifdef XFT_DEBUG_FONTSET XftFontSetPrint (_XftFontSet); #endif if (cache) XftFileCacheSave (cache); XftFileCacheDispose (); return True; }