xftglyphs.c   [plain text]


/*
 * $Id$
 *
 * Copyright © 2000 Keith Packard
 *
 * 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 "xftint.h"
#include <freetype/ftoutln.h>

static const int    filters[3][3] = {
    /* red */
#if 0
{    65538*4/7,65538*2/7,65538*1/7 },
    /* green */
{    65536*1/4, 65536*2/4, 65537*1/4 },
    /* blue */
{    65538*1/7,65538*2/7,65538*4/7 },
#endif
{    65538*9/13,65538*3/13,65538*1/13 },
    /* green */
{    65538*1/6, 65538*4/6, 65538*1/6 },
    /* blue */
{    65538*1/13,65538*3/13,65538*9/13 },
};

/*
 * Validate the memory info for a font
 */

static void
_XftFontValidateMemory (Display *dpy, XftFont *public)
{
    XftFontInt	    *font = (XftFontInt *) public;
    unsigned long   glyph_memory;
    FT_UInt	    glyphindex;
    XftGlyph	    *xftg;

    glyph_memory = 0;
    for (glyphindex = 0; glyphindex < font->num_glyphs; glyphindex++)
    {
	xftg = font->glyphs[glyphindex];
	if (xftg)
	{
	    glyph_memory += xftg->glyph_memory;
	}
    }
    if (glyph_memory != font->glyph_memory)
	printf ("Font glyph cache incorrect has %ld bytes, should have %ld\n",
		font->glyph_memory, glyph_memory);
}

_X_EXPORT void
XftFontLoadGlyphs (Display	    *dpy,
		   XftFont	    *pub,
		   FcBool	    need_bitmaps,
		   _Xconst FT_UInt  *glyphs,
		   int		    nglyph)
{
    XftDisplayInfo  *info = _XftDisplayInfoGet (dpy, True);
    XftFontInt	    *font = (XftFontInt *) pub;
    FT_Error	    error;
    FT_UInt	    glyphindex;
    FT_GlyphSlot    glyphslot;
    XftGlyph	    *xftg;
    Glyph	    glyph;
    unsigned char   bufLocal[4096];
    unsigned char   *bufBitmap = bufLocal;
    int		    bufSize = sizeof (bufLocal);
    int		    size, pitch;
    unsigned char   bufLocalRgba[4096];
    unsigned char   *bufBitmapRgba = bufLocalRgba;
    int		    bufSizeRgba = sizeof (bufLocalRgba);
    int		    sizergba, pitchrgba, widthrgba;
    int		    width;
    int		    height;
    int		    left, right, top, bottom;
    int		    hmul = 1;
    int		    vmul = 1;
    FT_Bitmap	    ftbit;
    FT_Matrix	    matrix;
    FT_Vector	    vector;
    Bool	    subpixel = False;
    FT_Face	    face;

    if (!info)
	return;

    face = XftLockFace (&font->public);
    
    if (!face)
	return;

    matrix.xx = matrix.yy = 0x10000L;
    matrix.xy = matrix.yx = 0;

    if (font->info.antialias)
    {
	switch (font->info.rgba) {
	case FC_RGBA_RGB:
	case FC_RGBA_BGR:
	    matrix.xx *= 3;
	    subpixel = True;
	    hmul = 3;
	    break;
	case FC_RGBA_VRGB:
	case FC_RGBA_VBGR:
	    matrix.yy *= 3;
	    vmul = 3;
	    subpixel = True;
	    break;
	}
    }

    while (nglyph--)
    {
	glyphindex = *glyphs++;
	xftg = font->glyphs[glyphindex];
	if (!xftg)
	    continue;
	
	if (XftDebug() & XFT_DBG_CACHE)
	    _XftFontValidateMemory (dpy, pub);
	/*
	 * Check to see if this glyph has just been loaded,
	 * this happens when drawing the same glyph twice
	 * in a single string
	 */
	if (xftg->glyph_memory)
	    continue;
	
	error = FT_Load_Glyph (face, glyphindex, font->info.load_flags);
	if (error)
	{
	    /*
	     * If anti-aliasing or transforming glyphs and
	     * no outline version exists, fallback to the
	     * bitmap and let things look bad instead of
	     * missing the glyph
	     */
	    if (font->info.load_flags & FT_LOAD_NO_BITMAP)
		error = FT_Load_Glyph (face, glyphindex,
				       font->info.load_flags & ~FT_LOAD_NO_BITMAP);
	    if (error)
		continue;
	}

#define FLOOR(x)    ((x) & -64)
#define CEIL(x)	    (((x)+63) & -64)
#define TRUNC(x)    ((x) >> 6)
#define ROUND(x)    (((x)+32) & -64)
		
	glyphslot = face->glyph;

#if HAVE_FT_GLYPHSLOT_EMBOLDEN
	/*
	 * Embolden if required
	 */
	if (font->info.embolden) FT_GlyphSlot_Embolden(glyphslot);
#endif

	/*
	 * Compute glyph metrics from FreeType information
	 */
	if(font->info.transform && glyphslot->format != ft_glyph_format_bitmap) 
	{
	    /*
	     * calculate the true width by transforming all four corners.
	     */
	    int xc, yc;
	    left = right = top = bottom = 0;
	    for(xc = 0; xc <= 1; xc ++) {
		for(yc = 0; yc <= 1; yc++) {
		    vector.x = glyphslot->metrics.horiBearingX + xc * glyphslot->metrics.width;
		    vector.y = glyphslot->metrics.horiBearingY - yc * glyphslot->metrics.height;
		    FT_Vector_Transform(&vector, &font->info.matrix);   
		    if (XftDebug() & XFT_DBG_GLYPH)
			printf("Trans %d %d: %d %d\n", (int) xc, (int) yc, 
			       (int) vector.x, (int) vector.y);
		    if(xc == 0 && yc == 0) {
			left = right = vector.x;
			top = bottom = vector.y;
		    } else {
			if(left > vector.x) left = vector.x;
			if(right < vector.x) right = vector.x;
			if(bottom > vector.y) bottom = vector.y;
			if(top < vector.y) top = vector.y;
		    }

		}
	    }
	    left = FLOOR(left);
	    right = CEIL(right);
	    bottom = FLOOR(bottom);
	    top = CEIL(top);

	} else {
	    left  = FLOOR( glyphslot->metrics.horiBearingX );
	    right = CEIL( glyphslot->metrics.horiBearingX + glyphslot->metrics.width );

	    top    = CEIL( glyphslot->metrics.horiBearingY );
	    bottom = FLOOR( glyphslot->metrics.horiBearingY - glyphslot->metrics.height );
	}

	width = TRUNC(right - left);
	height = TRUNC( top - bottom );

	/*
	 * Clip charcell glyphs to the bounding box
	 * XXX transformed?
	 */
	if (font->info.spacing >= FC_CHARCELL && !font->info.transform)
	{
	    if (font->info.load_flags & FT_LOAD_VERTICAL_LAYOUT)
	    {
		if (TRUNC(bottom) > font->public.max_advance_width)
		{
		    int adjust;
    
		    adjust = bottom - (font->public.max_advance_width << 6);
		    if (adjust > top)
			adjust = top;
		    top -= adjust;
		    bottom -= adjust;
		    height = font->public.max_advance_width;
		}
	    }
	    else
	    {
		if (TRUNC(right) > font->public.max_advance_width)
		{
		    int adjust;
    
		    adjust = right - (font->public.max_advance_width << 6);
		    if (adjust > left)
			adjust = left;
		    left -= adjust;
		    right -= adjust;
		    width = font->public.max_advance_width;
		}
	    }
	}

	if (font->info.antialias)
	    pitch = (width * hmul + 3) & ~3;
	else
	    pitch = ((width + 31) & ~31) >> 3;

	size = pitch * height * vmul;

	xftg->metrics.width = width;
	xftg->metrics.height = height;
	xftg->metrics.x = -TRUNC(left);
	xftg->metrics.y = TRUNC(top);

	if (font->info.spacing >= FC_MONO)
	{
	    if (font->info.transform)
	    {
		if (font->info.load_flags & FT_LOAD_VERTICAL_LAYOUT)
		{
		    vector.x = 0;
		    vector.y = -face->size->metrics.max_advance;
		}
		else
		{
		    vector.x = face->size->metrics.max_advance;
		    vector.y = 0;
		}
		FT_Vector_Transform (&vector, &font->info.matrix);
		xftg->metrics.xOff = vector.x >> 6;
		xftg->metrics.yOff = -(vector.y >> 6);
	    }
	    else
	    {
		if (font->info.load_flags & FT_LOAD_VERTICAL_LAYOUT)
		{
		    xftg->metrics.xOff = 0;
		    xftg->metrics.yOff = -font->public.max_advance_width;
		}
		else
		{
		    xftg->metrics.xOff = font->public.max_advance_width;
		    xftg->metrics.yOff = 0;
		}
	    }
	}
	else
	{
	    xftg->metrics.xOff = TRUNC(ROUND(glyphslot->advance.x));
	    xftg->metrics.yOff = -TRUNC(ROUND(glyphslot->advance.y));
	}
	
	/*
	 * If the glyph is relatively large (> 1% of server memory),
	 * don't send it until necessary
	 */
	if (!need_bitmaps && size > info->max_glyph_memory / 100)
	    continue;
	
	/*
	 * Make sure there's enough buffer space for the glyph
	 */
	if (size > bufSize)
	{
	    if (bufBitmap != bufLocal)
		free (bufBitmap);
	    bufBitmap = (unsigned char *) malloc (size);
	    if (!bufBitmap)
		continue;
	    bufSize = size;
	}
	memset (bufBitmap, 0, size);

	/*
	 * Rasterize into the local buffer
	 */
	switch (glyphslot->format) {
	case ft_glyph_format_outline:
	    ftbit.width      = width * hmul;
	    ftbit.rows       = height * vmul;
	    ftbit.pitch      = pitch;
	    if (font->info.antialias)
		ftbit.pixel_mode = ft_pixel_mode_grays;
	    else
		ftbit.pixel_mode = ft_pixel_mode_mono;
	    
	    ftbit.buffer     = bufBitmap;
	    
	    if (subpixel)
		FT_Outline_Transform (&glyphslot->outline, &matrix);

	    FT_Outline_Translate ( &glyphslot->outline, -left*hmul, -bottom*vmul );

	    FT_Outline_Get_Bitmap( _XftFTlibrary, &glyphslot->outline, &ftbit );
	    break;
	case ft_glyph_format_bitmap:
	    if (font->info.antialias)
	    {
		unsigned char	*srcLine, *dstLine;
		int		height;
		int		x;
		int	    h, v;

		srcLine = glyphslot->bitmap.buffer;
		dstLine = bufBitmap;
		height = glyphslot->bitmap.rows;
		while (height--)
		{
		    for (x = 0; x < glyphslot->bitmap.width; x++)
		    {
			/* always MSB bitmaps */
			unsigned char	a = ((srcLine[x >> 3] & (0x80 >> (x & 7))) ?
					     0xff : 0x00);
			if (subpixel)
			{
			    for (v = 0; v < vmul; v++)
				for (h = 0; h < hmul; h++)
				    dstLine[v * pitch + x*hmul + h] = a;
			}
			else
			    dstLine[x] = a;
		    }
		    dstLine += pitch * vmul;
		    srcLine += glyphslot->bitmap.pitch;
		}
	    }
	    else
	    {
		unsigned char	*srcLine, *dstLine;
		int		h, bytes;

		srcLine = glyphslot->bitmap.buffer;
		dstLine = bufBitmap;
		h = glyphslot->bitmap.rows;
		bytes = (glyphslot->bitmap.width + 7) >> 3;
		while (h--)
		{
		    memcpy (dstLine, srcLine, bytes);
		    dstLine += pitch;
		    srcLine += glyphslot->bitmap.pitch;
		}
	    }
	    break;
	default:
	    if (XftDebug() & XFT_DBG_GLYPH)
		printf ("glyph %d is not in a usable format\n",
			(int) glyphindex);
	    continue;
	}
	
	if (XftDebug() & XFT_DBG_GLYPH)
	{
	    printf ("glyph %d:\n", (int) glyphindex);
	    printf (" xywh (%d %d %d %d), trans (%d %d %d %d) wh (%d %d)\n",
		    (int) glyphslot->metrics.horiBearingX,
		    (int) glyphslot->metrics.horiBearingY,
		    (int) glyphslot->metrics.width,
		    (int) glyphslot->metrics.height,
		    left, right, top, bottom,
		    width, height);
	    if (XftDebug() & XFT_DBG_GLYPHV)
	    {
		int		x, y;
		unsigned char	*line;

		line = bufBitmap;
		for (y = 0; y < height * vmul; y++)
		{
		    if (font->info.antialias) 
		    {
			static char    den[] = { " .:;=+*#" };
			for (x = 0; x < pitch; x++)
			    printf ("%c", den[line[x] >> 5]);
		    }
		    else
		    {
			for (x = 0; x < pitch * 8; x++)
			{
			    printf ("%c", line[x>>3] & (1 << (x & 7)) ? '#' : ' ');
			}
		    }
		    printf ("|\n");
		    line += pitch;
		}
		printf ("\n");
	    }
	}

	/*
	 * Use the glyph index as the wire encoding; it
	 * might be more efficient for some locales to map
	 * these by first usage to smaller values, but that
	 * would require persistently storing the map when
	 * glyphs were freed.
	 */
	glyph = (Glyph) glyphindex;

	if (subpixel)
	{
	    int		    x, y;
	    unsigned char   *in_line, *out_line, *in;
	    unsigned int    *out;
	    unsigned int    red, green, blue;
	    int		    rf, gf, bf;
	    int		    s;
	    int		    o, os;
	    
	    /*
	     * Filter the glyph to soften the color fringes
	     */
	    widthrgba = width;
	    pitchrgba = (widthrgba * 4 + 3) & ~3;
	    sizergba = pitchrgba * height;

	    os = 1;
	    switch (font->info.rgba) {
	    case FC_RGBA_VRGB:
		os = pitch;
	    case FC_RGBA_RGB:
	    default:
		rf = 0;
		gf = 1;
		bf = 2;
		break;
	    case FC_RGBA_VBGR:
		os = pitch;
	    case FC_RGBA_BGR:
		bf = 0;
		gf = 1;
		rf = 2;
		break;
	    }
	    if (sizergba > bufSizeRgba)
	    {
		if (bufBitmapRgba != bufLocalRgba)
		    free (bufBitmapRgba);
		bufBitmapRgba = (unsigned char *) malloc (sizergba);
		if (!bufBitmapRgba)
		    continue;
		bufSizeRgba = sizergba;
	    }
	    memset (bufBitmapRgba, 0, sizergba);
	    in_line = bufBitmap;
	    out_line = bufBitmapRgba;
	    for (y = 0; y < height; y++)
	    {
		in = in_line;
		out = (unsigned int *) out_line;
		in_line += pitch * vmul;
		out_line += pitchrgba;
		for (x = 0; x < width * hmul; x += hmul)
		{
		    red = green = blue = 0;
		    o = 0;
		    for (s = 0; s < 3; s++)
		    {
			red += filters[rf][s]*in[x+o];
			green += filters[gf][s]*in[x+o];
			blue += filters[bf][s]*in[x+o];
			o += os;
		    }
		    red = red / 65536;
		    green = green / 65536;
		    blue = blue / 65536;
		    *out++ = (green << 24) | (red << 16) | (green << 8) | blue;
		}
	    }
	    
	    xftg->glyph_memory = sizergba + sizeof (XftGlyph);
	    if (font->format)
	    {
		if (!font->glyphset)
		    font->glyphset = XRenderCreateGlyphSet (dpy, font->format);
		if (ImageByteOrder (dpy) != XftNativeByteOrder ())
		    XftSwapCARD32 ((CARD32 *) bufBitmapRgba, sizergba >> 2);
		XRenderAddGlyphs (dpy, font->glyphset, &glyph,
				  &xftg->metrics, 1, 
				  (char *) bufBitmapRgba, sizergba);
	    }
	    else
	    {
		if (sizergba)
		{
		    xftg->bitmap = malloc (sizergba);
		    if (xftg->bitmap)
			memcpy (xftg->bitmap, bufBitmapRgba, sizergba);
		}
		else
		    xftg->bitmap = 0;
	    }
	}
	else
	{
	    xftg->glyph_memory = size + sizeof (XftGlyph);
	    if (font->format)
	    {
		/*
		 * swap bit order around; FreeType is always MSBFirst
		 */
		if (!font->info.antialias)
		{
		    if (BitmapBitOrder (dpy) != MSBFirst)
		    {
			unsigned char   *line;
			unsigned char   c;
			int		    i;

			line = (unsigned char *) bufBitmap;
			i = size;
			while (i--)
			{
			    c = *line;
			    c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
			    c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
			    c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
			    *line++ = c;
			}
		    }
		}
		if (!font->glyphset)
		    font->glyphset = XRenderCreateGlyphSet (dpy, font->format);
		XRenderAddGlyphs (dpy, font->glyphset, &glyph,
				  &xftg->metrics, 1, 
				  (char *) bufBitmap, size);
	    }
	    else
	    {
		if (size)
		{
		    xftg->bitmap = malloc (size);
		    if (xftg->bitmap)
			memcpy (xftg->bitmap, bufBitmap, size);
		}
		else
		    xftg->bitmap = 0;
	    }
	}
	font->glyph_memory += xftg->glyph_memory;
	info->glyph_memory += xftg->glyph_memory;
	if (XftDebug() & XFT_DBG_CACHE)
	    _XftFontValidateMemory (dpy, pub);
	if (XftDebug() & XFT_DBG_CACHEV)
	    printf ("Caching glyph 0x%x size %ld\n", glyphindex,
		    xftg->glyph_memory);
    }
    if (bufBitmap != bufLocal)
	free (bufBitmap);
    if (bufBitmapRgba != bufLocalRgba)
	free (bufBitmapRgba);
    XftUnlockFace (&font->public);
}

_X_EXPORT void
XftFontUnloadGlyphs (Display		*dpy,
		     XftFont		*pub,
		     _Xconst FT_UInt	*glyphs,
		     int		nglyph)
{
    XftDisplayInfo  *info = _XftDisplayInfoGet (dpy, False);
    XftFontInt	    *font = (XftFontInt *) pub;
    XftGlyph	    *xftg;
    FT_UInt	    glyphindex;
    Glyph	    glyphBuf[1024];
    int		    nused;
    
    nused = 0;
    while (nglyph--)
    {
	glyphindex = *glyphs++;
	xftg = font->glyphs[glyphindex];
	if (!xftg)
	    continue;
	if (xftg->glyph_memory)
	{
	    if (font->format)
	    {
		if (font->glyphset)
		{
		    glyphBuf[nused++] = (Glyph) glyphindex;
		    if (nused == sizeof (glyphBuf) / sizeof (glyphBuf[0]))
		    {
			XRenderFreeGlyphs (dpy, font->glyphset, glyphBuf, nused);
			nused = 0;
		    }
		}
	    }
	    else
	    {
		if (xftg->bitmap)
		    free (xftg->bitmap);
	    }
	    font->glyph_memory -= xftg->glyph_memory;
	    if (info)
		info->glyph_memory -= xftg->glyph_memory;
	}
	free (xftg);
	XftMemFree (XFT_MEM_GLYPH, sizeof (XftGlyph));
	font->glyphs[glyphindex] = 0;
    }    
    if (font->glyphset && nused)
	XRenderFreeGlyphs (dpy, font->glyphset, glyphBuf, nused);
}

_X_EXPORT FcBool
XftFontCheckGlyph (Display	*dpy,
		   XftFont	*pub,
		   FcBool	need_bitmaps,
		   FT_UInt	glyph,
		   FT_UInt	*missing,
		   int		*nmissing)
{
    XftFontInt	    *font = (XftFontInt *) pub;
    XftGlyph	    *xftg;
    int		    n;
    
    if (glyph >= font->num_glyphs)
	return FcFalse;
    xftg = font->glyphs[glyph];
    if (!xftg || (need_bitmaps && !xftg->glyph_memory))
    {
	if (!xftg)
	{
	    xftg = (XftGlyph *) malloc (sizeof (XftGlyph));
	    if (!xftg)
		return FcFalse;
	    XftMemAlloc (XFT_MEM_GLYPH, sizeof (XftGlyph));
	    xftg->bitmap = 0;
	    xftg->glyph_memory = 0;
	    font->glyphs[glyph] = xftg;
	}
	n = *nmissing;
	missing[n++] = glyph;
	if (n == XFT_NMISSING)
	{
	    XftFontLoadGlyphs (dpy, pub, need_bitmaps, missing, n);
	    n = 0;
	}
	*nmissing = n;
	return FcTrue;
    }
    else
	return FcFalse;
}

_X_EXPORT FcBool
XftCharExists (Display	    *dpy,
	       XftFont	    *pub,
	       FcChar32    ucs4)
{
    if (pub->charset)
	return FcCharSetHasChar (pub->charset, ucs4);
    return FcFalse;
}

#define Missing	    ((FT_UInt) ~0)

_X_EXPORT FT_UInt
XftCharIndex (Display	    *dpy, 
	      XftFont	    *pub,
	      FcChar32	    ucs4)
{
    XftFontInt	*font = (XftFontInt *) pub;
    FcChar32	ent, offset;
    FT_Face	face;
    
    if (!font->hash_value)
	return 0;

    ent = ucs4 % font->hash_value;
    offset = 0;
    while (font->hash_table[ent].ucs4 != ucs4)
    {
	if (font->hash_table[ent].ucs4 == (FcChar32) ~0)
	{
	    if (!XftCharExists (dpy, pub, ucs4))
		return 0;
	    face  = XftLockFace (pub);
	    if (!face)
		return 0;
	    font->hash_table[ent].ucs4 = ucs4;
	    font->hash_table[ent].glyph = FcFreeTypeCharIndex (face, ucs4);
	    XftUnlockFace (pub);
	    break;
	}
	if (!offset)
	{
	    offset = ucs4 % font->rehash_value;
	    if (!offset)
		offset = 1;
	}
	ent = ent + offset;
	if (ent >= font->hash_value)
	    ent -= font->hash_value;
    }
    return font->hash_table[ent].glyph;
}

/*
 * Pick a random glyph from the font and remove it from the cache
 */
_X_HIDDEN void
_XftFontUncacheGlyph (Display *dpy, XftFont *pub)
{
    XftFontInt	    *font = (XftFontInt *) pub;
    unsigned long   glyph_memory;
    FT_UInt	    glyphindex;
    XftGlyph	    *xftg;
    
    if (!font->glyph_memory)
	return;
    if (font->use_free_glyphs)
    {
	glyph_memory = rand() % font->glyph_memory;
    }
    else
    {
	if (font->glyphset)
	{
	    XRenderFreeGlyphSet (dpy, font->glyphset);
	    font->glyphset = 0;
	}
	glyph_memory = 0;
    }
	
    if (XftDebug() & XFT_DBG_CACHE)
	_XftFontValidateMemory (dpy, pub);
    for (glyphindex = 0; glyphindex < font->num_glyphs; glyphindex++)
    {
	xftg = font->glyphs[glyphindex];
	if (xftg)
	{
	    if (xftg->glyph_memory > glyph_memory)
	    {
		if (XftDebug() & XFT_DBG_CACHEV)
		    printf ("Uncaching glyph 0x%x size %ld\n",
			    glyphindex, xftg->glyph_memory);
		XftFontUnloadGlyphs (dpy, pub, &glyphindex, 1);
		if (!font->use_free_glyphs)
		    continue;
		break;
	    }
	    glyph_memory -= xftg->glyph_memory;
	}
    }
    if (XftDebug() & XFT_DBG_CACHE)
	_XftFontValidateMemory (dpy, pub);
}

_X_HIDDEN void
_XftFontManageMemory (Display *dpy, XftFont *pub)
{
    XftFontInt	*font = (XftFontInt *) pub;

    if (font->max_glyph_memory)
    {
	if (XftDebug() & XFT_DBG_CACHE)
	{
	    if (font->glyph_memory > font->max_glyph_memory)
		printf ("Reduce memory for font 0x%lx from %ld to %ld\n",
			font->glyphset ? font->glyphset : (unsigned long) font,
			font->glyph_memory, font->max_glyph_memory);
	}
	while (font->glyph_memory > font->max_glyph_memory)
	    _XftFontUncacheGlyph (dpy, pub);
    }
    _XftDisplayManageMemory (dpy);
}