/*
Copyright (c) 1997 by Mark Leisher
Copyright (c) 1998-2003 by Juliusz Chroboczek
Copyright (c) 1998 Go Watanabe, All rights reserved.
Copyright (c) 1998 Kazushi (Jam) Marukawa, All rights reserved.
Copyright (c) 1998 Takuya SHIOZAKI, All rights reserved.
Copyright (c) 1998 X-TrueType Server Project, All rights reserved.
Copyright (c) 2003 After X-TT Project, All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* $XdotOrg: xc/lib/font/FreeType/ftfuncs.c,v 1.1.4.1.2.2 2004/03/29 12:18:05 ago Exp $ */

/* $XFree86: xc/lib/font/FreeType/ftfuncs.c,v 1.43 2004/02/07 04:37:18 dawes Exp $ */

#include "fontmisc.h"

#ifndef FONTMODULE
#include <string.h>
#include <math.h>
#else
#include "Xmd.h"
#include "Xdefs.h"
#include "xf86_ansic.h"
#endif

#include "fntfilst.h"
#include "fontutil.h"
#include "FSproto.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_SIZES_H
#include FT_TRUETYPE_IDS_H
#include FT_TRUETYPE_TABLES_H
#include FT_TYPE1_TABLES_H
#include FT_XFREE86_H
#include FT_BBOX_H
#include FT_INTERNAL_TRUETYPE_TYPES_H
#ifdef USE_INTERNAL_FREETYPE
# include "ttobjs.h"
#endif

/*
 *  If you want to use FT_Outline_Get_CBox instead of 
 *  FT_Outline_Get_BBox, define here.
 */
/* #define USE_GET_CBOX */
#ifdef USE_GET_CBOX
#include FT_OUTLINE_H
#endif

#include "fontenc.h"
#include "ft.h"
#include "ftfuncs.h"
#include "xttcap.h"

/* Work around FreeType bug */
#define WORK_AROUND_UPM 2048

#ifndef True
#define True (-1)
#endif /* True */
#ifndef False
#define False (0)
#endif /* False */

#define FLOOR64(x) ((x) & -64)
#define CEIL64(x) (((x) + 64 - 1) & -64)

/*
 *  If you want very lazy method(vl=y) AS DEFAULT when
 *  handling large charset, define here.
 */
/* #define DEFAULT_VERY_LAZY 1 */     	/* Always */
#define DEFAULT_VERY_LAZY 2     	/* Multi-byte only */
/* #define DEFAULT_VERY_LAZY 256 */   	/* Unicode only */

/* Does the X accept noSuchChar? */
#define X_ACCEPTS_NO_SUCH_CHAR
/* Does the XAA accept NULL noSuchChar.bits?(dangerous) */
/* #define XAA_ACCEPTS_NULL_BITS */

#ifdef X_ACCEPTS_NO_SUCH_CHAR
static CharInfoRec noSuchChar = { /* metrics */{0,0,0,0,0,0},
				  /* bits */   NULL };
#endif

/* The propery names for all the XLFD properties. */

static char *xlfd_props[] = {
    "FOUNDRY",
    "FAMILY_NAME",
    "WEIGHT_NAME",
    "SLANT",
    "SETWIDTH_NAME",
    "ADD_STYLE_NAME",
    "PIXEL_SIZE",
    "POINT_SIZE",
    "RESOLUTION_X",
    "RESOLUTION_Y",
    "SPACING",
    "AVERAGE_WIDTH",
    "CHARSET_REGISTRY",
    "CHARSET_ENCODING",
};


static int ftypeInitP = 0;      /* is the engine initialised? */
static FT_Library ftypeLibrary;

static FTFacePtr faceTable[NUMFACEBUCKETS];

static unsigned
hash(char *string)
{
    int i;
    unsigned u = 0;
    for(i = 0; string[i] != '\0'; i++)
        u = (u<<5) + (u >> (NUMFACEBUCKETS - 5)) + (unsigned char)string[i];
    return u;
}

static int
ifloor(int x, int y)
{
    if(x >= 0)
        return x/y;
    else
        return x/y - 1;
}

static int
iceil(int x, int y)
{
    return ifloor(x + y - 1, y);
}
  
static int
FreeTypeOpenFace(FTFacePtr *facep, char *FTFileName, char *realFileName, int faceNumber)
{
    FT_Error ftrc;
    int bucket;
    FTFacePtr face, otherFace;

    if (!ftypeInitP) {
        ftrc = FT_Init_FreeType(&ftypeLibrary);
        if (ftrc != 0) {
            ErrorF("FreeType: error initializing ftypeEngine: %d\n", ftrc);
            return AllocError;
        }
        ftypeInitP = 1;
    }

    /* Try to find a matching face in the hashtable */
    bucket = hash(FTFileName)%NUMFACEBUCKETS;
    otherFace = faceTable[bucket];
    while(otherFace) {
        if( strcmp(otherFace->filename, FTFileName) == 0 ) break;
        otherFace = otherFace->next;
    }
    if(otherFace) {
        MUMBLE1("Returning cached face: %s\n", otherFace->filename);
        *facep = otherFace;
        return Successful;
    }

    /* No cached match; need to make a new one */
    face = (FTFacePtr)xalloc(sizeof(FTFaceRec));
    if(face == NULL) {
        return AllocError;
    }
    memset(face, 0, sizeof(FTFaceRec));

    face->filename = (char*)xalloc(strlen(FTFileName)+1);
    if(face->filename == NULL) {
        xfree(face);
        return AllocError;
    }
    strcpy(face->filename, FTFileName);

    ftrc = FT_New_Face(ftypeLibrary, realFileName, faceNumber, &face->face);
    if(ftrc != 0) {
        ErrorF("FreeType: couldn't open face %s: %d\n", FTFileName, ftrc);
        xfree(face->filename);
        xfree(face);
        return BadFontName;
    }

    face->bitmap = ((face->face->face_flags & FT_FACE_FLAG_SCALABLE) == 0);
    if(!face->bitmap) {
        TT_MaxProfile *maxp;
        maxp = FT_Get_Sfnt_Table(face->face, ft_sfnt_maxp);
        if(maxp && maxp->maxContours == 0)
            face->bitmap = 1;
    }
    /* Insert face in hashtable and return it */
    face->next = faceTable[bucket];
    faceTable[bucket] = face;
    *facep = face;
    return Successful;
}

static void
FreeTypeFreeFace(FTFacePtr face)
{
    int bucket;
    FTFacePtr otherFace;

    if(!face->instances) {
        bucket = hash(face->filename) % NUMFACEBUCKETS;
        if(faceTable[bucket] == face)
            faceTable[bucket] = face->next;
        else {
            otherFace = faceTable[bucket];
            while(otherFace) {
                if(otherFace->next == face)
                    break;
                otherFace = otherFace->next;
            }
            if(otherFace && otherFace->next)
                otherFace->next = otherFace->next->next;
            else
                ErrorF("FreeType: freeing unknown face\n");
        }
        MUMBLE1("Closing face: %s\n", face->filename);
        FT_Done_Face(face->face);
        xfree(face->filename);
        xfree(face);
    }
}

static int
TransEqual(FTNormalisedTransformationPtr t1, FTNormalisedTransformationPtr t2)
{
    if(t1->scale != t2->scale)
        return 0;
    else if(t1->xres != t2->xres || t1->yres != t2->yres)
        return 0;
    else if(t1->nonIdentity != t2->nonIdentity)
        return 0;
    else if(t1->nonIdentity && t2->nonIdentity) {
        return 
            t1->matrix.xx == t2->matrix.xx &&
            t1->matrix.yx == t2->matrix.yx &&
            t1->matrix.yy == t2->matrix.yy &&
            t1->matrix.xy == t2->matrix.xy;
    } else
        return 1;
}

static int
BitmapFormatEqual(FontBitmapFormatPtr f1, FontBitmapFormatPtr f2)
{
    return
        f1->bit == f2->bit &&
        f1->byte == f2->byte &&
        f1->glyph == f2->glyph;
}

static int
TTCapEqual(struct TTCapInfo *t1, struct TTCapInfo *t2)
{
    return
	t1->autoItalic == t2->autoItalic &&
	t1->scaleWidth == t2->scaleWidth &&
	t1->scaleBBoxWidth == t2->scaleBBoxWidth &&
	t1->scaleBBoxHeight == t2->scaleBBoxHeight &&
	t1->doubleStrikeShift == t2->doubleStrikeShift &&
	t1->adjustBBoxWidthByPixel == t2->adjustBBoxWidthByPixel &&
	t1->adjustLeftSideBearingByPixel == t2->adjustLeftSideBearingByPixel &&
	t1->adjustRightSideBearingByPixel == t2->adjustRightSideBearingByPixel &&
	t1->flags == t2->flags &&
	t1->scaleBitmap == t2->scaleBitmap &&
	/*
	  If we use forceConstantSpacing, 
	  we *MUST* allocate new instance.
	*/
	t1->forceConstantSpacingEnd < 0 &&
	t2->forceConstantSpacingEnd < 0;
}

static int
FTInstanceMatch(FTInstancePtr instance,
		char *FTFileName, FTNormalisedTransformationPtr trans,
		int spacing, FontBitmapFormatPtr bmfmt,
		struct TTCapInfo *tmp_ttcap, FT_Int32 load_flags) 
{
    if(strcmp(instance->face->filename, FTFileName) != 0) {
        return 0;
    } else if(!TransEqual(&instance->transformation, trans)) {
        return 0;
    } else if( spacing != instance->spacing ) {
        return 0;
    } else if( load_flags != instance->load_flags ) {
        return 0;
    } else if(!BitmapFormatEqual(&instance->bmfmt, bmfmt)) {
        return 0;
    } else if(!TTCapEqual(&instance->ttcap, tmp_ttcap)) {
        return 0;
    } else {
        return 1;
    }
}

static int
FreeTypeActivateInstance(FTInstancePtr instance)
{
    FT_Error ftrc;
    if(instance->face->active_instance == instance)
        return Successful;

    ftrc = FT_Activate_Size(instance->size);
    if(ftrc != 0) {
        instance->face->active_instance = NULL;
        ErrorF("FreeType: couldn't activate instance: %d\n", ftrc);
        return FTtoXReturnCode(ftrc);
    }
    FT_Set_Transform(instance->face->face,
                     instance->transformation.nonIdentity ?
                     &instance->transformation.matrix : 0,
                     0);
                            
    instance->face->active_instance = instance;
    return Successful;
}

static int
FTFindSize(FT_Face face, FTNormalisedTransformationPtr trans,
           int *x_return, int *y_return)
{
    int tx, ty, x, y;
    int i, j;
    int d, dd;

    if(trans->nonIdentity)
        return BadFontName;

    tx = (int)(trans->scale * trans->xres / 72.0 + 0.5);
    ty = (int)(trans->scale * trans->yres / 72.0 + 0.5);

    d = 100;
    j = -1;
    for(i = 0; i < face->num_fixed_sizes; i++) {
        x = face->available_sizes[i].width;
        y = face->available_sizes[i].height;
        if(ABS(x - tx) <= 1 && ABS(y - ty) <= 1) {
            dd = ABS(x - tx) * ABS(x - tx) + ABS(y - ty) * ABS(y - ty);
            if(dd < d) {
                j = i;
                d = dd;
            }
        }            
    }
    if(j < 0)
        return BadFontName;

    *x_return = face->available_sizes[j].width;
    *y_return = face->available_sizes[j].height;
    return Successful;
}

static int
FreeTypeOpenInstance(FTInstancePtr *instance_return, FTFacePtr face,
                     char *FTFileName, FTNormalisedTransformationPtr trans,
                     int spacing, FontBitmapFormatPtr bmfmt,
		     struct TTCapInfo *tmp_ttcap, FT_Int32 load_flags)
{
    FT_Error ftrc;
    int xrc;
    FTInstancePtr instance, otherInstance;

    /* Search for a matching instance */
    for(otherInstance = face->instances;
        otherInstance;
        otherInstance = otherInstance->next) {
        if(FTInstanceMatch(otherInstance, FTFileName, trans, spacing, bmfmt,
			   tmp_ttcap, load_flags)) break;
    }
    if(otherInstance) {
        MUMBLE("Returning cached instance\n");
        otherInstance->refcount++;
        *instance_return = otherInstance;
        return Successful;
    }

    /* None matching found */
    instance = (FTInstancePtr)xalloc(sizeof(FTInstanceRec));
    if(instance == NULL) {
        return AllocError;
    }

    instance->refcount = 1;
    instance->face = face;

    instance->load_flags = load_flags;
    instance->spacing    = spacing;		/* Actual spacing */
    instance->pixel_size =0;
    instance->pixel_width_unit_x =0;
    instance->pixel_width_unit_y =0;
    instance->charcellMetrics = NULL;
    instance->averageWidth = 0;
    instance->rawAverageWidth = 0;
    instance->forceConstantMetrics = NULL;

    instance->transformation = *trans;
    instance->bmfmt = *bmfmt;
    instance->glyphs = NULL;
    instance->available = NULL;

    if( 0 <= tmp_ttcap->forceConstantSpacingEnd )
	instance->nglyphs = 2 * instance->face->face->num_glyphs;
    else
	instance->nglyphs = instance->face->face->num_glyphs;

    /* Store the TTCap info. */
    memcpy((char*)&instance->ttcap, (char*)tmp_ttcap,
	   sizeof(struct TTCapInfo));

    ftrc = FT_New_Size(instance->face->face, &instance->size);
    if(ftrc != 0) {
        ErrorF("FreeType: couldn't create size object: %d\n", ftrc);
        xfree(instance);
        return FTtoXReturnCode(ftrc);
    }
    FreeTypeActivateInstance(instance);
    if(!face->bitmap) {
        ftrc = FT_Set_Char_Size(instance->face->face,
                                (int)(trans->scale*(1<<6) + 0.5),
                                (int)(trans->scale*(1<<6) + 0.5),
                                trans->xres, trans->yres);
    } else {
        int xsize, ysize;
        xrc = FTFindSize(face->face, trans, &xsize, &ysize);
        if(xrc != Successful) {
            xfree(instance);
            return xrc;
        }
        ftrc = FT_Set_Pixel_Sizes(instance->face->face, xsize, ysize);
    }
    if(ftrc != 0) {
        FT_Done_Size(instance->size);
        xfree(instance);
        return FTtoXReturnCode(ftrc);
    }

    /* maintain a linked list of instances */
    instance->next = instance->face->instances;
    instance->face->instances = instance;
    
    *instance_return = instance;
    return Successful;
}

static void
FreeTypeFreeInstance(FTInstancePtr instance)
{
    FTInstancePtr otherInstance;

    if( instance == NULL ) return;

    if(instance->face->active_instance == instance)
        instance->face->active_instance = NULL;
    instance->refcount--;
    if(instance->refcount <= 0) {
        int i,j;
        
        if(instance->face->instances == instance)
            instance->face->instances = instance->next;
        else {
            for(otherInstance = instance->face->instances;
                otherInstance;
                otherInstance = otherInstance->next)
                if(otherInstance->next == instance) {
                    otherInstance->next = instance->next;
                    break;
                }
        }

        FT_Done_Size(instance->size);
        FreeTypeFreeFace(instance->face);

        if(instance->charcellMetrics) {
            xfree(instance->charcellMetrics);
        }
        if(instance->forceConstantMetrics) {
            xfree(instance->forceConstantMetrics);
        }
        if(instance->glyphs) {
            for(i = 0; i < iceil(instance->nglyphs, FONTSEGMENTSIZE); i++) {
                if(instance->glyphs[i]) {
                    for(j = 0; j < FONTSEGMENTSIZE; j++) {
                        if(instance->available[i][j] == 
                           FT_AVAILABLE_RASTERISED)
                            xfree(instance->glyphs[i][j].bits);
                    }
                    xfree(instance->glyphs[i]);
                }
            }
            xfree(instance->glyphs);
        }
        if(instance->available) {
            for(i = 0; i < iceil(instance->nglyphs, FONTSEGMENTSIZE); i++) {
                if(instance->available[i])
                    xfree(instance->available[i]);
            }
            xfree(instance->available);
        }
        xfree(instance);
    }
}

static int
FreeTypeInstanceFindGlyph(unsigned idx_in, int flags, FTInstancePtr instance,
                          CharInfoPtr **glyphs, int ***available,
                          int *found, int *segmentP, int *offsetP)
{
    int segment, offset;
    unsigned idx = idx_in;

    if( 0 <= instance->ttcap.forceConstantSpacingEnd ){
	if( (flags & FT_FORCE_CONSTANT_SPACING) )
	    idx += instance->nglyphs / 2 ;
    }

    if(idx > instance->nglyphs) {
        *found = 0;
        return Successful;
    }
  
    if(*available == NULL) {
        *available = 
            (int**)xalloc(sizeof(int*) * iceil(instance->nglyphs, 
                                               FONTSEGMENTSIZE));
        if(*available == NULL)
            return AllocError;
        memset((char*)(*available), 0, 
               sizeof(int*) * iceil(instance->nglyphs, FONTSEGMENTSIZE));
    }

    segment = ifloor(idx, FONTSEGMENTSIZE);
    offset = idx - segment * FONTSEGMENTSIZE;

    if((*available)[segment] == NULL) {
        (*available)[segment] = (int*)xalloc(sizeof(int) * FONTSEGMENTSIZE);
        if((*available)[segment] == NULL)
            return AllocError;
        memset((char*)(*available)[segment], 0, sizeof(int) * FONTSEGMENTSIZE);
    }

    if(*glyphs == NULL) {
        *glyphs = (CharInfoPtr*)xalloc(sizeof(CharInfoPtr)*
                                       iceil(instance->nglyphs, 
                                             FONTSEGMENTSIZE));
        if(*glyphs == NULL)
            return AllocError;
        memset((char*)(*glyphs), 0, 
               sizeof(CharInfoPtr)*iceil(instance->nglyphs, FONTSEGMENTSIZE));
    }

    if((*glyphs)[segment] == NULL) {
        (*glyphs)[segment]=
            (CharInfoPtr)xalloc(sizeof(CharInfoRec) * FONTSEGMENTSIZE);
        if((*glyphs)[segment] == NULL)
            return AllocError;
    }

    *found = 1;
    *segmentP = segment;
    *offsetP = offset;
    return Successful;
}

static int
FreeTypeInstanceGetGlyph(unsigned idx, int flags, CharInfoPtr *g, FTInstancePtr instance)
{
    int found, segment, offset;
    int xrc;
    int ***available;
    CharInfoPtr **glyphs;
    
    available = &instance->available;
    glyphs = &instance->glyphs;

    xrc = FreeTypeInstanceFindGlyph(idx, flags, instance, glyphs, available,
                                    &found, &segment, &offset);
    if(xrc != Successful)
        return xrc;

    if(!found || (*available)[segment][offset] == FT_AVAILABLE_NO) {
        *g = NULL;
        return Successful;
    } 

    if((*available)[segment][offset] == FT_AVAILABLE_RASTERISED) {
	*g = &(*glyphs)[segment][offset];
	return Successful;
    }
    
    flags |= FT_GET_GLYPH_BOTH;

    xrc = FreeTypeRasteriseGlyph(idx, flags, 
				 &(*glyphs)[segment][offset], instance, 
				 (*available)[segment][offset] >= FT_AVAILABLE_METRICS);
    if(xrc != Successful && (*available)[segment][offset] >= FT_AVAILABLE_METRICS) {
	ErrorF("Warning: FreeTypeRasteriseGlyph() returns an error,\n");
	ErrorF("\tso the backend tries to set a white space.\n");
	xrc = FreeTypeRasteriseGlyph(idx, flags | FT_GET_DUMMY,
				     &(*glyphs)[segment][offset], instance,
				     (*available)[segment][offset] >= FT_AVAILABLE_METRICS);
    }
    if(xrc == Successful) {
        (*available)[segment][offset] = FT_AVAILABLE_RASTERISED;
	/* return the glyph */
        *g = &(*glyphs)[segment][offset];
    }
    return xrc;
}

static int
FreeTypeInstanceGetGlyphMetrics(unsigned idx, int flags,
				xCharInfo **metrics, FTInstancePtr instance )
{
    int xrc;
    int found, segment, offset;
    
    /* Char cell */
    if(instance->spacing == FT_CHARCELL) {
	*metrics = instance->charcellMetrics;
	return Successful;
    }
    /* Force constant metrics  */
    if( flags & FT_FORCE_CONSTANT_SPACING) {
	*metrics = instance->forceConstantMetrics;
	return Successful;
    }

    /* Not char cell */

    xrc = FreeTypeInstanceFindGlyph(idx, flags, instance, 
                                    &instance->glyphs, &instance->available, 
                                    &found, &segment, &offset);
    if(xrc != Successful)
        return xrc;
    if(!found) {
        *metrics = NULL;
        return Successful;
    }
    if( instance->available[segment][offset] == FT_AVAILABLE_NO ) {
        *metrics = NULL;
        return Successful;
    } 

    if( instance->available[segment][offset] >= FT_AVAILABLE_METRICS ) {
	*metrics = &instance->glyphs[segment][offset].metrics;
	return Successful;
    }
    
    flags |= FT_GET_GLYPH_METRICS_ONLY;

    xrc = FreeTypeRasteriseGlyph(idx, flags, 
				 &instance->glyphs[segment][offset],
				 instance, 0);
    if(xrc == Successful) {
        instance->available[segment][offset] = FT_AVAILABLE_METRICS;
        *metrics = &instance->glyphs[segment][offset].metrics;
    }
    return xrc;
}
    
/*
 * Pseudo enbolding similar as Microsoft Windows.
 * It is useful but poor.
 */
static void
ft_make_up_bold_bitmap( char *raster, int bpr, int ht, int ds_mode)
{
    int x, y;
    unsigned char *p = (unsigned char *)raster;
    if ( ds_mode & TTCAP_DOUBLE_STRIKE_MKBOLD_EDGE_LEFT ) {
        for (y=0; y<ht; y++) {
            unsigned char rev_pat=0;
            unsigned char lsb = 0;
            for (x=0; x<bpr; x++) {
                unsigned char tmp = *p<<7;
                if ( (rev_pat & 0x01) && (*p & 0x80) ) p[-1] &= 0xfe;
                rev_pat = ~(*p);
                *p |= (*p>>1) | lsb;
                *p &= ~(rev_pat & (*p << 1));
                lsb = tmp;
                p++;
            }
        }
    }
    else {
        for (y=0; y<ht; y++) {
            unsigned char lsb = 0;
            for (x=0; x<bpr; x++) {
                unsigned char tmp = *p<<7;
                *p |= (*p>>1) | lsb;
                lsb = tmp;
                p++;
            }
        }
    }
}

static void
ft_make_up_italic_bitmap( char *raster, int bpr, int ht, int shift,
			  int h_total, int h_offset, double a_italic)
{
    int x, y;
    unsigned char *p = (unsigned char *)raster;
    if ( a_italic < 0 ) shift = -shift;
    for (y=0; y<ht; y++) {
        unsigned char *tmp_p = p + y*bpr;
        int tmp_shift = shift * (h_total -1 -(y+h_offset)) / h_total;
        int tmp_byte_shift;
	if ( 0 <= tmp_shift ) {
	    tmp_byte_shift = tmp_shift/8;
	    tmp_shift %= 8;
	    if ( tmp_shift ) {
		for (x=bpr-1;0<=x;x--) {
		    if ( x != bpr-1 ) 
			tmp_p[x+1] |= tmp_p[x]<<(8-tmp_shift);
		    tmp_p[x]>>=tmp_shift;
		}
	    }
	    if ( tmp_byte_shift ) {
		for (x=bpr-1;0<x;x--) {
		    tmp_p[x] = tmp_p[x-1];
		}
		tmp_p[x]=0;
	    }
	}
	else {
	    tmp_shift = -tmp_shift;
	    tmp_byte_shift = tmp_shift/8;
	    tmp_shift %= 8;
	    if ( tmp_shift ) {
		for (x=0;x<bpr;x++) {
		    if ( x != 0 ) 
			tmp_p[x-1] |= tmp_p[x]>>(8-tmp_shift);
		    tmp_p[x]<<=tmp_shift;
		}
	    }
	    if ( tmp_byte_shift ) {
		for (x=0;x<bpr-1;x++) {
		    tmp_p[x] = tmp_p[x+1];
		}
		tmp_p[x]=0;
	    }
	}
    }
}

/*
 * The very lazy method,
 * parse the htmx field in TrueType font.
 */

/* from src/truetype/ttgload.c */
static void
tt_get_metrics( TT_HoriHeader*  header,
		FT_UInt         idx,
		FT_Short*       bearing,
		FT_UShort*      advance )
/*  Copyright 1996-2001, 2002 by                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg. */
{
    TT_LongMetrics  longs_m;
    FT_UShort       k = header->number_Of_HMetrics;

    if ( k == 0 ) {
	*bearing = *advance = 0;
	return;
    }

    if ( idx < (FT_UInt)k ) {
	longs_m  = (TT_LongMetrics )header->long_metrics + idx;
	*bearing = longs_m->bearing;
	*advance = longs_m->advance;
    }
    else {
	*bearing = ((TT_ShortMetrics*)header->short_metrics)[idx - k];
	*advance = ((TT_LongMetrics )header->long_metrics)[k - 1].advance;
    }
}

static int                   
ft_get_very_lazy_bbox( FT_UInt index,
		       FT_Face face,
		       FT_Size size,
		       double slant,
		       FT_Matrix *matrix,
		       FT_BBox *bbox,
		       FT_Long *horiAdvance,
		       FT_Long *vertAdvance)
{
    if ( FT_IS_SFNT( face ) ) {
	TT_Face   ttface = (TT_Face)face;
	FT_Size_Metrics *smetrics = &size->metrics;
	FT_Short  leftBearing = 0;
	FT_UShort advance = 0;
	FT_Vector p0, p1, p2, p3;

	/* horizontal */
	tt_get_metrics(&ttface->horizontal, index,
		       &leftBearing, &advance);

#if 0
	fprintf(stderr,"x_scale=%f y_scale=%f\n",
		(double)smetrics->x_scale,(double)smetrics->y_scale);
#endif
	bbox->xMax = *horiAdvance =
	    FT_MulFix( advance, smetrics->x_scale );
	bbox->xMin =
	    FT_MulFix( leftBearing, smetrics->x_scale );
	/* vertical */
	bbox->yMin = FT_MulFix( face->bbox.yMin,
				smetrics->y_scale );
	bbox->yMax = FT_MulFix( face->bbox.yMax,
				smetrics->y_scale );
	/* slant */
	if( 0 < slant ) {
	    bbox->xMax += slant * bbox->yMax;
	    bbox->xMin += slant * bbox->yMin;
	}
	else if( slant < 0 ) {
	    bbox->xMax += slant * bbox->yMin;
	    bbox->xMin += slant * bbox->yMax;
	}

	*vertAdvance = -1;	/* We don't support */

	p0.x = p2.x = bbox->xMin;
	p1.x = p3.x = bbox->xMax;
	p0.y = p1.y = bbox->yMin;
	p2.y = p3.y = bbox->yMax;

	FT_Vector_Transform(&p0, matrix);
	FT_Vector_Transform(&p1, matrix);
	FT_Vector_Transform(&p2, matrix);
	FT_Vector_Transform(&p3, matrix);

#if 0
	fprintf(stderr,
		"->(%.1f %.1f) (%.1f %.1f)"
		"  (%.1f %.1f) (%.1f %.1f)\n",
		p0.x / 64.0, p0.y / 64.0,
		p1.x / 64.0, p1.y / 64.0,
		p2.x / 64.0, p2.y / 64.0,
		p3.x / 64.0, p3.y / 64.0);
#endif
	bbox->xMin = MIN(p0.x, MIN(p1.x, MIN(p2.x, p3.x)));
	bbox->xMax = MAX(p0.x, MAX(p1.x, MAX(p2.x, p3.x)));
	bbox->yMin = MIN(p0.y, MIN(p1.y, MIN(p2.y, p3.y)));
	bbox->yMax = MAX(p0.y, MAX(p1.y, MAX(p2.y, p3.y)));
	return 0;	/* Successful */
    }
    return -1;
}

int
FreeTypeRasteriseGlyph(unsigned idx, int flags, CharInfoPtr tgp,
		       FTInstancePtr instance, int hasMetrics)
{
    FTFacePtr face;
    FT_Bitmap *bitmap;
    FT_BBox bbox;
    FT_Long outline_hori_advance;
    FT_Long outline_vert_advance;
    char *raster = NULL;
    FT_Glyph_Metrics *metrics = NULL;
    int wd, ht, bpr;            /* width, height, bytes per row */
    int wd_actual, ht_actual;
    int ftrc, is_outline, b_shift=0, correct=0;
    int dx, dy;
    int leftSideBearing, rightSideBearing, characterWidth, rawCharacterWidth,
        ascent, descent;
    double bbox_center_raw;

    face = instance->face;

    FreeTypeActivateInstance(instance);

    bitmap = &face->face->glyph->bitmap;

    if(!tgp) return AllocError;

    if( instance->spacing == FT_CHARCELL ) correct=1;
    else if( flags & FT_FORCE_CONSTANT_SPACING ) correct=1;
    else if( instance->ttcap.flags & TTCAP_IS_VERY_LAZY ){
	if( hasMetrics || (!hasMetrics && (flags & FT_GET_GLYPH_METRICS_ONLY)) ){
	    /* If sbit is available, we don't use very lazy method. */
	    /* See TT_Load_Glyph */
	    if( FT_IS_SFNT( face->face ) ) {
#ifdef USE_INTERNAL_FREETYPE
		TT_Size tt_size = (TT_Size)instance->size;
		if( !( !(instance->load_flags & FT_LOAD_NO_BITMAP)
		       && tt_size->strike_index != 0xFFFFU ) )
#else
		if((instance->load_flags & FT_LOAD_NO_BITMAP)
		   || (face->face->face_flags & FT_FACE_FLAG_FIXED_SIZES) == 0)
#endif
		    correct=1;
	    }
	}
    }

    /*
     * PREPARE METRICS
     */

    if(!hasMetrics) {
	if( instance->spacing == FT_CHARCELL || flags & FT_GET_DUMMY ){
	    memcpy((char*)&tgp->metrics, 
		   (char*)instance->charcellMetrics,
		   sizeof(xCharInfo));
	}
	else if( flags & FT_FORCE_CONSTANT_SPACING ) {
	    memcpy((char*)&tgp->metrics, 
		   (char*)instance->forceConstantMetrics,
		   sizeof(xCharInfo));
	}
	/* mono or prop. */
	else{
	    int new_width, try_very_lazy=correct;
	    double ratio;

	    if( try_very_lazy ) {
		if( ft_get_very_lazy_bbox( idx, face->face, instance->size, 
					   instance->ttcap.vl_slant,
					   &instance->transformation.matrix,
					   &bbox, &outline_hori_advance, 
					   &outline_vert_advance ) == 0 ) {
		    goto bbox_ok;	/* skip exact calculation */
		}
	    }

	    ftrc = FT_Load_Glyph(instance->face->face, idx, 
				 instance->load_flags);
	    metrics = &face->face->glyph->metrics;

	    if(ftrc != 0) return FTtoXReturnCode(ftrc);

	    if( face->face->glyph->format == FT_GLYPH_FORMAT_BITMAP ) {
		/* bitmap, embedded bitmap */
		leftSideBearing = metrics->horiBearingX / 64;
		rightSideBearing = (metrics->width + metrics->horiBearingX) / 64;
		bbox_center_raw = (2.0 * metrics->horiBearingX + metrics->width)/2.0/64.0;
		characterWidth = (int)floor(metrics->horiAdvance 
					    * instance->ttcap.scaleBBoxWidth / 64.0 + .5);
		ascent = metrics->horiBearingY / 64;
		descent = (metrics->height - metrics->horiBearingY) / 64 ;
		/* */
		new_width = characterWidth;
		if( instance->ttcap.flags & TTCAP_DOUBLE_STRIKE_CORRECT_B_BOX_WIDTH )
		    new_width += instance->ttcap.doubleStrikeShift;
		new_width += instance->ttcap.adjustBBoxWidthByPixel;
		ratio = (double)new_width/characterWidth;
		characterWidth = new_width;
		/* adjustment by pixel unit */
		if( instance->ttcap.flags & TTCAP_DOUBLE_STRIKE )
		    rightSideBearing += instance->ttcap.doubleStrikeShift;
		rightSideBearing += instance->ttcap.adjustRightSideBearingByPixel;
		leftSideBearing  += instance->ttcap.adjustLeftSideBearingByPixel;
		rightSideBearing += instance->ttcap.rsbShiftOfBitmapAutoItalic;
		leftSideBearing  += instance->ttcap.lsbShiftOfBitmapAutoItalic;
		/* */
		rawCharacterWidth =
		    (unsigned short)(short)(floor(1000 * metrics->horiAdvance 
						  * instance->ttcap.scaleBBoxWidth * ratio / 64.
						  / instance->pixel_size));
	    }
	    else {
		/* Outline */
#ifdef USE_GET_CBOX
		/* Very fast?? */
		FT_Outline_Get_CBox(&face->face->glyph->outline, &bbox);
		ftrc=0;		/* FT_Outline_Get_CBox returns nothing. */
#else
		/* Calculate exact metrics */
		ftrc=FT_Outline_Get_BBox(&face->face->glyph->outline, &bbox);
#endif
		if( ftrc != 0 ) return FTtoXReturnCode(ftrc);
		outline_hori_advance = metrics->horiAdvance;
		outline_vert_advance = metrics->vertAdvance;
	    bbox_ok:
		descent  = CEIL64(-bbox.yMin - 32) / 64;
		leftSideBearing  = FLOOR64(bbox.xMin + 32) / 64;
		ascent   = FLOOR64(bbox.yMax + 32) / 64;
		rightSideBearing = FLOOR64(bbox.xMax + 32) / 64;
		bbox_center_raw = (double)(bbox.xMax + bbox.xMin)/2.0/64.;
		if ( instance->pixel_width_unit_x != 0 )
		    characterWidth =
			(int)floor( outline_hori_advance 
				    * instance->ttcap.scaleBBoxWidth
				    * instance->pixel_width_unit_x / 64. + .5);
		else {
		    characterWidth = 
			(int)floor( outline_vert_advance
				    * instance->ttcap.scaleBBoxHeight
				    * instance->pixel_width_unit_y / 64. + .5);
		    if(characterWidth <= 0)
			characterWidth = instance->charcellMetrics->characterWidth;
		}
		/* */
		new_width = characterWidth;
		if( instance->ttcap.flags & TTCAP_DOUBLE_STRIKE_CORRECT_B_BOX_WIDTH )
		    new_width += instance->ttcap.doubleStrikeShift;
		new_width += instance->ttcap.adjustBBoxWidthByPixel;
		ratio = (double)new_width/characterWidth;
		characterWidth = new_width;
		if ( instance->pixel_width_unit_x != 0 )
		    rawCharacterWidth =
			(unsigned short)(short)(floor(1000 * outline_hori_advance 
						      * instance->ttcap.scaleBBoxWidth * ratio
						      * instance->pixel_width_unit_x / 64.));
		else {
		    rawCharacterWidth =
			(unsigned short)(short)(floor(1000 * outline_vert_advance 
						      * instance->ttcap.scaleBBoxHeight * ratio
						      * instance->pixel_width_unit_y / 64.));
		    if(rawCharacterWidth <= 0)
			rawCharacterWidth = instance->charcellMetrics->attributes;
		}
		/* adjustment by pixel unit */
		if( instance->ttcap.flags & TTCAP_DOUBLE_STRIKE )
		    rightSideBearing += instance->ttcap.doubleStrikeShift;
		rightSideBearing += instance->ttcap.adjustRightSideBearingByPixel;
		leftSideBearing  += instance->ttcap.adjustLeftSideBearingByPixel;
	    }

	    /* Set the glyph metrics. */
	    tgp->metrics.attributes = (unsigned short)((short)rawCharacterWidth);
	    tgp->metrics.leftSideBearing = leftSideBearing;
	    tgp->metrics.rightSideBearing = rightSideBearing;
	    tgp->metrics.characterWidth = characterWidth;
	    tgp->metrics.ascent = ascent;
	    tgp->metrics.descent = descent;
	    /* Update the width to match the width of the font */
	    if( instance->spacing != FT_PROPORTIONAL )
		tgp->metrics.characterWidth = instance->charcellMetrics->characterWidth;
	    if(instance->ttcap.flags & TTCAP_MONO_CENTER){
		b_shift   = (int)floor((instance->advance/2.0-bbox_center_raw) + .5);
		tgp->metrics.leftSideBearing  += b_shift;
		tgp->metrics.rightSideBearing += b_shift;
	    }
	}
    }

    if( flags & FT_GET_GLYPH_METRICS_ONLY ) return Successful;

    /*
     * RENDER AND ALLOCATE BUFFER
     */

    if( flags & FT_GET_DUMMY ) is_outline = -1;
    else {
	if( !metrics ) {
	    ftrc = FT_Load_Glyph(instance->face->face, idx, 
				 instance->load_flags);
	    metrics = &face->face->glyph->metrics;

	    if(ftrc != 0) return FTtoXReturnCode(ftrc);
	}

	if( face->face->glyph->format != FT_GLYPH_FORMAT_BITMAP ) {
	    ftrc = FT_Render_Glyph(face->face->glyph,FT_RENDER_MODE_MONO);
	    if( ftrc != 0 ) return FTtoXReturnCode(ftrc);
	    is_outline = 1;
	}
	else{
	    is_outline=0;
	}
    }

    /* Spacial case */
    if( (instance->ttcap.flags & TTCAP_MONO_CENTER) && hasMetrics ) {
	if( is_outline == 1 ){
	    if( correct ){
		if( ft_get_very_lazy_bbox( idx, face->face, instance->size, 
					   instance->ttcap.vl_slant,
					   &instance->transformation.matrix,
					   &bbox, &outline_hori_advance, 
					   &outline_vert_advance ) != 0 ){
		    is_outline = -1;	/* <- error */
		}
	    }
	    else {
#ifdef USE_GET_CBOX
		FT_Outline_Get_CBox(&face->face->glyph->outline, &bbox);
		ftrc=0;
#else
		ftrc=FT_Outline_Get_BBox(&face->face->glyph->outline, &bbox);
#endif
		if( ftrc != 0 ) return FTtoXReturnCode(ftrc);
	    }
	    bbox_center_raw = (double)(bbox.xMax + bbox.xMin)/2.0/64.;
	}
	else if( is_outline == 0 )
	    bbox_center_raw = (2.0 * metrics->horiBearingX + metrics->width)/2.0/64.0;
	else
	    bbox_center_raw = 0;
	b_shift = (int)floor((instance->advance/2.0-bbox_center_raw) + .5);
    }

    wd_actual = tgp->metrics.rightSideBearing - tgp->metrics.leftSideBearing;
    ht_actual = tgp->metrics.ascent + tgp->metrics.descent;

    /* The X convention is to consider a character with an empty
     * bounding box as undefined.  This convention is broken. */

    if(wd_actual <= 0) wd = 1;
    else wd=wd_actual;
    if(ht_actual <= 0) ht = 1;
    else ht=ht_actual;

    bpr = (((wd + (instance->bmfmt.glyph<<3) - 1) >> 3) & 
           -instance->bmfmt.glyph);
    raster = (char*)xalloc(ht * bpr);
    if(raster == NULL) 
	return AllocError;
    memset(raster, 0, ht * bpr);

    tgp->bits = raster;

    /* If FT_GET_DUMMY is set, we return white space. */
    if ( is_outline == -1 ) return Successful;

    if ( wd_actual <= 0 || ht_actual <= 0 ) return Successful;

    /*
     * CALCULATE OFFSET, dx AND dy.
     */

    dx = face->face->glyph->bitmap_left - tgp->metrics.leftSideBearing;
    dy = tgp->metrics.ascent - face->face->glyph->bitmap_top;

    if(instance->ttcap.flags & TTCAP_MONO_CENTER)
	dx += b_shift;

    /* To prevent chipped bitmap, we correct dx and dy if needed. */
    if( correct && is_outline==1 ){
	int lsb, rsb, asc, des;
	int chip_left,chip_right,chip_top,chip_bot;
#ifdef USE_GET_CBOX
	FT_Outline_Get_CBox(&face->face->glyph->outline, &bbox);
	ftrc=0;
#else
	ftrc=FT_Outline_Get_BBox(&face->face->glyph->outline, &bbox);
#endif
	if( ftrc != 0 ) return FTtoXReturnCode(ftrc);
	des = CEIL64(-bbox.yMin - 32) / 64;
	lsb = FLOOR64(bbox.xMin + 32) / 64;
	asc = FLOOR64(bbox.yMax + 32) / 64;
	rsb = FLOOR64(bbox.xMax + 32) / 64;
	rightSideBearing = tgp->metrics.rightSideBearing;
	leftSideBearing  = tgp->metrics.leftSideBearing;
	if( instance->ttcap.flags & TTCAP_DOUBLE_STRIKE )
	    rightSideBearing -= instance->ttcap.doubleStrikeShift;
	/* special case */
	if(instance->ttcap.flags & TTCAP_MONO_CENTER){
	    leftSideBearing  -= b_shift;
	    rightSideBearing -= b_shift;
	}
	chip_left  = lsb - leftSideBearing;
	chip_right = rightSideBearing - rsb;
	if( flags & FT_FORCE_CONSTANT_SPACING ){
	    if( instance->ttcap.force_c_adjust_lsb_by_pixel != 0 ||
		instance->ttcap.force_c_adjust_rsb_by_pixel != 0 ){
		chip_left=0;
		chip_right=0;
	    }
	}
	else{
	    if( instance->ttcap.adjustRightSideBearingByPixel != 0 ||
		instance->ttcap.adjustLeftSideBearingByPixel != 0 ){
		chip_left=0;
		chip_right=0;
	    }
	}
	chip_top   = tgp->metrics.ascent - asc;
	chip_bot   = tgp->metrics.descent - des;
	if( chip_left < 0 && 0 < chip_right ) dx++;
	else if( chip_right < 0 && 0 < chip_left ) dx--;
	if( chip_top < 0 && 0 < chip_bot ) dy++;
	else if( chip_bot < 0 && 0 < chip_top ) dy--;
    }

    /*
     * COPY RASTER
     */

    {
	int i, j;
	unsigned char *current_raster;
	unsigned char *current_buffer;
	int mod_dx0,mod_dx1;
	int div_dx;
	if( 0 <= dx ){
	    div_dx = dx / 8;
	    mod_dx0 = dx % 8;
	    mod_dx1 = 8-mod_dx0;
	}
	else{
	    div_dx = dx / 8 -1;
	    mod_dx1 = -dx % 8;
	    mod_dx0 = 8-mod_dx1;
	}
	for( i = MAX(0, dy) ; i<ht ; i++ ){
	    int prev_jj,jj;
	    if( bitmap->rows <= i-dy ) break;
	    current_buffer=(unsigned char *)(bitmap->buffer+bitmap->pitch*(i-dy));
	    current_raster=(unsigned char *)(raster+i*bpr);
	    j       = MAX(0,div_dx);
	    jj      = j-div_dx;
	    prev_jj = jj-1;
	    if( j<bpr ){
		if( 0 <= prev_jj && prev_jj < bitmap->pitch )
		    current_raster[j]|=current_buffer[prev_jj]<<mod_dx1;
		if( 0 <= jj && jj < bitmap->pitch ){
		    current_raster[j]|=current_buffer[jj]>>mod_dx0;
		    j++; prev_jj++; jj++;
		    for( ; j<bpr ; j++,prev_jj++,jj++ ){
			current_raster[j]|=current_buffer[prev_jj]<<mod_dx1;
			if( bitmap->pitch <= jj ) break;
			current_raster[j]|=current_buffer[jj]>>mod_dx0;
		    }
		}
	    }
	}
    }

    /* by TTCap */
    if ( instance->ttcap.flags & TTCAP_DOUBLE_STRIKE ) {
	int i;
	for( i=0 ; i < instance->ttcap.doubleStrikeShift ; i++ )
	    ft_make_up_bold_bitmap( raster, bpr, ht, instance->ttcap.flags);
    }
    if ( is_outline == 0 &&
	 ( instance->ttcap.lsbShiftOfBitmapAutoItalic != 0 ||
	   instance->ttcap.rsbShiftOfBitmapAutoItalic != 0 ) ) {
	ft_make_up_italic_bitmap( raster, bpr, ht, 
				  - instance->ttcap.lsbShiftOfBitmapAutoItalic
				  + instance->ttcap.rsbShiftOfBitmapAutoItalic,
				  instance->charcellMetrics->ascent
				  + instance->charcellMetrics->descent,
				  instance->charcellMetrics->ascent
				  - tgp->metrics.ascent,
				  instance->ttcap.autoItalic);
    }
    
    if(instance->bmfmt.bit == LSBFirst) {
        BitOrderInvert((unsigned char*)(tgp->bits), ht*bpr);
    }
    
    if(instance->bmfmt.byte != instance->bmfmt.bit) {
        switch(instance->bmfmt.scan) {
        case 1:
            break;
        case 2:
            TwoByteSwap((unsigned char*)(tgp->bits), ht*bpr);
            break;
        case 4:
            FourByteSwap((unsigned char*)(tgp->bits), ht*bpr);
            break;
        default:
            ;
        }
    }

    return Successful;
}

static void
FreeTypeFreeFont(FTFontPtr font)
{
    FreeTypeFreeInstance(font->instance);
    if(font->ranges)
        xfree(font->ranges);
    if(font->dummy_char.bits)
	xfree(font->dummy_char.bits);
    xfree(font);
}

/* Free a font.  If freeProps is 0, don't free the properties. */

static void
FreeTypeFreeXFont(FontPtr pFont, int freeProps)
{
    FTFontPtr tf;
    
    if(pFont) {
        if((tf = (FTFontPtr)pFont->fontPrivate)) {
            FreeTypeFreeFont(tf);
        }
        if(freeProps && pFont->info.nprops>0) {
            xfree(pFont->info.isStringProp);
            xfree(pFont->info.props);
        }
        DestroyFontRec(pFont);
    }
}


/* Unload a font */

static void
FreeTypeUnloadXFont(FontPtr pFont)
{
    MUMBLE("Unloading\n");
    FreeTypeFreeXFont(pFont, 1);
}

/* Add the font properties, including the Font name, the XLFD
   properties, some strings from the font, and various typographical
   data.  We only provide data readily available in the tables in the
   font for now, altough FIGURE_WIDTH would be a good idea as it is
   used by Xaw. */

static int
FreeTypeAddProperties(FTFontPtr font, FontScalablePtr vals, FontInfoPtr info, 
                      char *fontname, int rawAverageWidth, Bool font_properties)
{
    int i, j, maxprops;
    char *sp, *ep, val[MAXFONTNAMELEN], *vp;
    FTFacePtr face;
    FTInstancePtr instance;
    FTNormalisedTransformationPtr trans;
    int upm;
    TT_OS2 *os2;
    TT_Postscript *post;
    PS_FontInfoRec t1info_rec, *t1info;
    int xlfdProps = 0;
    int ftrc;

    instance = font->instance;
    face = instance->face;
    trans = &instance->transformation;
    upm = face->face->units_per_EM;
    if(upm == 0) {
        /* Work around FreeType bug */
        upm = WORK_AROUND_UPM;
    }

    os2 = FT_Get_Sfnt_Table(face->face, ft_sfnt_os2);
    post = FT_Get_Sfnt_Table(face->face, ft_sfnt_post);
    ftrc = FT_Get_PS_Font_Info(face->face, &t1info_rec);
    if(ftrc == 0)
        t1info = &t1info_rec;
    else
        t1info = NULL;

    if(t1info) {
        os2 = NULL;
        post = NULL;
    }

    info->nprops = 0;           /* in case we abort */
  
    strcpy(val, fontname);
    if(FontParseXLFDName(val, vals, FONT_XLFD_REPLACE_VALUE)) {
        xlfdProps = 1;
    } else {
        MUMBLE("Couldn't parse XLFD\n");
        xlfdProps = 0;
    }

    maxprops=
        1 +                     /* NAME */
        (xlfdProps ? 14 : 0) +  /* from XLFD */
        5 +
        ( !face->bitmap ? 3 : 0 ) +	/* raw_av,raw_asc,raw_dec */
        ( font_properties ? 2 : 0 ) +	/* asc,dec */
        ( (font_properties && os2) ? 6 : 0 ) +
        ( (font_properties && (post || t1info)) ? 3 : 0 ) +
        2;                      /* type */
    
    info->props = (FontPropPtr)xalloc(maxprops * sizeof(FontPropRec));
    if(info->props == NULL)
        return AllocError;
    
    info->isStringProp = (char*)xalloc(maxprops);
    if(info->isStringProp == NULL) {
        xfree(info->props);
        return AllocError;
    }

    memset((char *)info->isStringProp, 0, maxprops);

    i = 0;

    info->props[i].name = MakeAtom("FONT", 4, TRUE);
    info->props[i].value = MakeAtom(val, strlen(val), TRUE);
    info->isStringProp[i] = 1;
    i++;

    if(*val && *(sp = val + 1)) {
        for (j = 0, sp = val + 1; j < 14; j++) {
            if (j == 13)
                /* Handle the case of the final field containing a subset
                   specification. */
                for (ep = sp; *ep && *ep != '['; ep++);
            else
                for (ep = sp; *ep && *ep != '-'; ep++);
            
            info->props[i].name =
                MakeAtom(xlfd_props[j], strlen(xlfd_props[j]), TRUE);
            
            switch(j) {
            case 6:                   /* pixel size */
                info->props[i].value = 
                    (int)(fabs(vals->pixel_matrix[3]) + 0.5);
                i++;
                break;
            case 7:                   /* point size */
                info->props[i].value = 
                    (int)(fabs(vals->point_matrix[3])*10.0 + 0.5);
                i++;
                break;
            case 8:                   /* resolution x */
                info->props[i].value = vals->x;
                i++;
                break;
            case 9:                   /* resolution y */
                info->props[i].value = vals->y;
                i++;
                break;
            case 11:                  /* average width */
                info->props[i].value = vals->width;
                i++;
                break;
            default:                  /* a string */
                info->props[i].value = MakeAtom(sp, ep - sp, TRUE);
                info->isStringProp[i] = 1;
                i++;
            }
            sp = ++ep;
        }
    }

    info->props[i].name = MakeAtom("RAW_PIXEL_SIZE", 14, TRUE);
    info->props[i].value = 1000;
    i++;

    info->props[i].name = MakeAtom("RAW_POINT_SIZE", 14, TRUE);
    info->props[i].value = (long)(72270.0 / (double)vals->y + .5);
    i++;

    if(!face->bitmap) {
        info->props[i].name = MakeAtom("RAW_AVERAGE_WIDTH", 17, TRUE);
        info->props[i].value = rawAverageWidth;
        i++;
    }

    if ( font_properties ) {
	info->props[i].name = MakeAtom("FONT_ASCENT", 11, TRUE);
	info->props[i].value = info->fontAscent;
	i++;
    }

    if(!face->bitmap) {
        info->props[i].name = MakeAtom("RAW_ASCENT", 10, TRUE);
        info->props[i].value = 
            ((double)face->face->ascender/(double)upm*1000.0);
        i++;
    }

    if ( font_properties ) {
	info->props[i].name = MakeAtom("FONT_DESCENT", 12, TRUE);
	info->props[i].value = info->fontDescent;
	i++;
    }

    if(!face->bitmap) {
        info->props[i].name = MakeAtom("RAW_DESCENT", 11, TRUE);
        info->props[i].value = 
            -((double)face->face->descender/(double)upm*1000.0);
        i++;
    }

    j = FTGetEnglishName(face->face, TT_NAME_ID_COPYRIGHT,
                         val, MAXFONTNAMELEN);
    vp = val;
    if (j < 0) {
        if(t1info && t1info->notice) {
            vp = t1info->notice;
            j = strlen(vp);
        }
    }
    if(j > 0) {
        info->props[i].name = MakeAtom("COPYRIGHT", 9, TRUE);
        info->props[i].value = MakeAtom(vp, j, TRUE);
        info->isStringProp[i] = 1;
        i++;
    }
    
    j = FTGetEnglishName(face->face, TT_NAME_ID_FULL_NAME,
                         val, MAXFONTNAMELEN);
    vp = val;
    if (j < 0) {
        if(t1info && t1info->full_name) {
            vp = t1info->full_name;
            j = strlen(vp);
        }
    }
    if(j > 0) {
        info->props[i].name = MakeAtom("FACE_NAME", 9, TRUE);
        info->props[i].value = MakeAtom(vp, j, TRUE);
        info->isStringProp[i] = 1;
        i++;
    }

    vp = (char *)FT_Get_Postscript_Name(face->face);
    if (vp) {
	j = strlen(vp);
    } else {
	j = -1;
    }
    if (j < 0) {
	j = FTGetEnglishName(face->face, TT_NAME_ID_PS_NAME,
                         val, MAXFONTNAMELEN);
	vp = val;
    }
    if (j < 0) {
        if(t1info && t1info->full_name) {
            vp = t1info->full_name;
            j = strlen(vp);
        }
    }
    if(j > 0) {
        info->props[i].name = MakeAtom("_ADOBE_POSTSCRIPT_FONTNAME", 26, TRUE);
        info->props[i].value = MakeAtom(vp, j, TRUE);
        info->isStringProp[i] = 1;
        i++;
    }

  /* These macros handle the case of a diagonal matrix.  They convert
     FUnits into pixels. */
#define TRANSFORM_FUNITS_X(xval) \
  ((int) \
   floor( ((double)(xval)/(double)upm) * (double)vals->pixel_matrix[0] + 0.5 ) )

#define TRANSFORM_FUNITS_Y(yval) \
  ((int) \
   floor( ((double)(yval)/(double)upm) * (double)vals->pixel_matrix[3] + 0.5 ) )

  /* In what follows, we assume the matrix is diagonal.  In the rare
     case when it is not, the values will be somewhat wrong. */
  
    if( font_properties && os2 ) {
        info->props[i].name = MakeAtom("SUBSCRIPT_SIZE",14,TRUE);
        info->props[i].value = 
            TRANSFORM_FUNITS_Y(os2->ySubscriptYSize);
        i++;
        info->props[i].name = MakeAtom("SUBSCRIPT_X",11,TRUE);
        info->props[i].value = 
            TRANSFORM_FUNITS_X(os2->ySubscriptXOffset);
        i++;
        info->props[i].name = MakeAtom("SUBSCRIPT_Y",11,TRUE);
        info->props[i].value = 
            TRANSFORM_FUNITS_Y(os2->ySubscriptYOffset);
        i++;
        info->props[i].name = MakeAtom("SUPERSCRIPT_SIZE",16,TRUE);
        info->props[i].value = 
            TRANSFORM_FUNITS_Y(os2->ySuperscriptYSize);
        i++;
        info->props[i].name = MakeAtom("SUPERSCRIPT_X",13,TRUE);
        info->props[i].value = 
            TRANSFORM_FUNITS_X(os2->ySuperscriptXOffset);
        i++;
        info->props[i].name = MakeAtom("SUPERSCRIPT_Y",13,TRUE);
        info->props[i].value = 
        TRANSFORM_FUNITS_Y(os2->ySuperscriptYOffset);
        i++;
    }

    if( font_properties && (post || t1info) ) {
        int underlinePosition, underlineThickness;

	/* Raw underlineposition counts upwards, 
	   but UNDERLINE_POSITION counts downwards. */
        if(post) {
            underlinePosition = TRANSFORM_FUNITS_Y(-post->underlinePosition);
            underlineThickness = TRANSFORM_FUNITS_Y(post->underlineThickness);
        } else {
            underlinePosition = 
                TRANSFORM_FUNITS_Y(-t1info->underline_position);
            underlineThickness = 
                TRANSFORM_FUNITS_Y(t1info->underline_thickness);
        }
        if(underlineThickness <= 0)
            underlineThickness = 1;

        info->props[i].name = MakeAtom("UNDERLINE_THICKNESS",19,TRUE);
        info->props[i].value = underlineThickness;
        i++;

        info->props[i].name = MakeAtom("UNDERLINE_POSITION",18,TRUE);

        info->props[i].value = underlinePosition;

        i++;

        /* The italic angle is often unreliable for Type 1 fonts */
        if(post && trans->matrix.xx == trans->matrix.yy) {
            info->props[i].name = MakeAtom("ITALIC_ANGLE",12,TRUE);
            info->props[i].value = 
                /* Convert from TT_Fixed to 
                   64th of a degree counterclockwise from 3 o'clock */
                90*64+(post->italicAngle >> 10);
            i++;
        }
#undef TRANSFORM_FUNITS_X
#undef TRANSFORM_FUNITS_Y
    }

    info->props[i].name  = MakeAtom("FONT_TYPE", 9, TRUE);
    vp = (char *)FT_Get_X11_Font_Format(face->face);
    info->props[i].value = MakeAtom(vp, strlen(vp), TRUE);
    info->isStringProp[i] = 1;
    i++;
    
    info->props[i].name  = MakeAtom("RASTERIZER_NAME", 15, TRUE);
    info->props[i].value = MakeAtom("FreeType", 10, TRUE);
    info->isStringProp[i] = 1;
    i++;
    
    info->nprops = i;
    return Successful;
}

static int
ft_get_index(unsigned code, FTFontPtr font, unsigned *idx)
{
    
    /* As a special case, we pass 0 even when it is not in the ranges;
       this will allow for the default glyph, which should exist in any
       TrueType font. */
    
    /* This is not required...
    if(code > 0 && font->nranges) {
        int i;
        for(i = 0; i < font->nranges; i++)
            if((code >= 
                font->ranges[i].min_char_low+
                (font->ranges[i].min_char_high<<8)) &&
               (code <=
                font->ranges[i].max_char_low + 
                (font->ranges[i].max_char_high<<8)))
                break;
        if(i == font->nranges) {
	    *idx = font->zero_idx;
            return -1;
        }
    }
    */
    if( font->info ) {
	if( !( font->info->firstCol <= (code & 0x000ff) &&
	       (code & 0x000ff) <= font->info->lastCol &&
	       font->info->firstRow <= (code >> 8) &&
	       (code >> 8) <= font->info->lastRow ) ) {
	    *idx = font->zero_idx;
	    /* Error: The code has not been parsed in ft_compute_bounds()!
	       We should not return any metrics. */
	    return -1;
	}
    }

    *idx = FTRemap(font->instance->face->face, &font->mapping, code);
    
    return 0;
}

static int
FreeTypeFontGetGlyph(unsigned code, int flags, CharInfoPtr *g, FTFontPtr font)
{
    unsigned idx = 0;
    int xrc;
    
#ifdef X_ACCEPTS_NO_SUCH_CHAR
    if( ft_get_index(code,font,&idx) || idx == 0 || idx == font->zero_idx ) {
	*g = NULL;
	flags &= ~FT_FORCE_CONSTANT_SPACING;
	/* if( font->instance->spacing != FT_CHARCELL ) */
	return Successful;
    }
#else
    if( ft_get_index(code,font,&idx) ) {
	/* The code has not been parsed! */
	*g = NULL;
	flags &= ~FT_FORCE_CONSTANT_SPACING;
    }
#endif

    xrc = FreeTypeInstanceGetGlyph(idx, flags, g, font->instance);
    if( xrc == Successful && *g != NULL )
	return Successful;
    if( font->zero_idx != idx ) {
	xrc = FreeTypeInstanceGetGlyph(font->zero_idx, flags, g, font->instance);
	if( xrc == Successful && *g != NULL )
	    return Successful;
    }
    return FreeTypeInstanceGetGlyph(font->zero_idx, flags|FT_GET_DUMMY, g, font->instance);
}

static int
FreeTypeFontGetGlyphMetrics(unsigned code, int flags, xCharInfo **metrics, FTFontPtr font)
{
    unsigned idx = 0;
    int xrc;

#ifdef X_ACCEPTS_NO_SUCH_CHAR
    if ( ft_get_index(code,font,&idx) || idx == 0 || idx == font->zero_idx ) {
	*metrics = NULL;
	flags &= ~FT_FORCE_CONSTANT_SPACING;
	/* if( font->instance->spacing != FT_CHARCELL ) */
	return Successful;
    }
#else
    if ( ft_get_index(code,font,&idx) || idx == 0 || idx == font->zero_idx ) {
	/* The code has not been parsed! */
	*metrics = NULL;
	flags &= ~FT_FORCE_CONSTANT_SPACING;
    }
#endif

    xrc = FreeTypeInstanceGetGlyphMetrics(idx, flags, metrics, font->instance);
    if( xrc == Successful && *metrics != NULL )
	return Successful;
    if( font->zero_idx != idx ) {
	xrc = FreeTypeInstanceGetGlyphMetrics(font->zero_idx, flags, 
					      metrics, font->instance);
	if( xrc == Successful && *metrics != NULL )
	    return Successful;
    }
    return FreeTypeInstanceGetGlyphMetrics(font->zero_idx, flags|FT_GET_DUMMY, metrics, font->instance);
}

/*
 * restrict code range
 *
 * boolean for the numeric zone:
 *   results = results & (ranges[0] | ranges[1] | ... ranges[nranges-1])
 */

static void
restrict_code_range(unsigned short *refFirstCol,
                    unsigned short *refFirstRow,
                    unsigned short *refLastCol,
                    unsigned short *refLastRow,
                    fsRange const *ranges, int nRanges)
{
    if (nRanges) {
        int minCol = 256, minRow = 256, maxCol = -1, maxRow = -1;
        fsRange const *r = ranges;
        int i;

        for (i=0; i<nRanges; i++) {
            if (r->min_char_high != r->max_char_high) {
                minCol = 0x00;
                maxCol = 0xff;
            } else {
                if (minCol > r->min_char_low)
                    minCol = r->min_char_low;
                if (maxCol < r->max_char_low)
                    maxCol = r->max_char_low;
            }
            if (minRow > r->min_char_high)
                minRow = r->min_char_high;
            if (maxRow < r->max_char_high)
                maxRow = r->max_char_high;
            r++;
        }

        if (minCol > *refLastCol)
            *refFirstCol = *refLastCol;
        else if (minCol > *refFirstCol)
            *refFirstCol = minCol;

        if (maxCol < *refFirstCol)
            *refLastCol = *refFirstCol;
        else if (maxCol < *refLastCol)
            *refLastCol = maxCol;

        if (minRow > *refLastRow) {
            *refFirstRow = *refLastRow;
            *refFirstCol = *refLastCol;
        } else if (minRow > *refFirstRow)
            *refFirstRow = minRow;

        if (maxRow < *refFirstRow) {
            *refLastRow = *refFirstRow;
            *refLastCol = *refFirstCol;
        } else if (maxRow < *refLastRow)
            *refLastRow = maxRow;
    }
}


static int
restrict_code_range_by_str(int count,unsigned short *refFirstCol,
			   unsigned short *refFirstRow,
			   unsigned short *refLastCol,
			   unsigned short *refLastRow,
			   char const *str)
{
    int nRanges = 0;
    int result = 0;
    fsRange *ranges = NULL;
    char const *p, *q;

    p = q = str;
    for (;;) {
        int minpoint=0, maxpoint=65535;
        long val;

        /* skip comma and/or space */
        while (',' == *p || isspace(*p))
            p++;

        /* begin point */
        if ('-' != *p) {
            val = strtol(p, (char **)&q, 0);
            if (p == q)
                /* end or illegal */
                break;
            if (val<0 || val>65535) {
                /* out of zone */
                break;
            }
            minpoint = val;
            p=q;
        }

        /* skip space */
        while (isspace(*p))
            p++;

        if (',' != *p && '\0' != *p) {
            /* contiune */
            if ('-' == *p)
                /* hyphon */
                p++;
            else
                /* end or illegal */
                break;

            /* skip space */
            while (isspace(*p))
                p++;

            val = strtol(p, (char **)&q, 0);
            if (p != q) {
                if (val<0 || val>65535)
                    break;
                maxpoint = val;
            } else if (',' != *p && '\0' != *p)
                /* end or illegal */
                break;
            p=q;
        } else
            /* comma - single code */
            maxpoint = minpoint;

        if ( count <= 0 && minpoint>maxpoint ) {
            int tmp;
            tmp = minpoint;
            minpoint = maxpoint;
            maxpoint = tmp;
        }

        /* add range */
#if 0
        fprintf(stderr, "zone: 0x%04X - 0x%04X\n", minpoint, maxpoint);
        fflush(stderr);
#endif
        nRanges++;
        ranges = (fsRange *)xrealloc(ranges, nRanges*sizeof(*ranges));
        if (NULL == ranges)
            break;
        {
            fsRange *r = ranges+nRanges-1;

            r->min_char_low = minpoint & 0xff;
            r->max_char_low = maxpoint & 0xff;
            r->min_char_high = (minpoint>>8) & 0xff;
            r->max_char_high = (maxpoint>>8) & 0xff;
        }
    }

    if (ranges) {
        if ( count <= 0 ) {
            restrict_code_range(refFirstCol, refFirstRow, refLastCol, refLastRow,
                                ranges, nRanges);
        }
        else {
            int i;
            fsRange *r;
            for ( i=0 ; i<nRanges ; i++ ) {
                if ( count <= i ) break;
                r = ranges+i;
                refFirstCol[i] = r->min_char_low;
                refLastCol[i] = r->max_char_low;
                refFirstRow[i] = r->min_char_high;
                refLastRow[i] = r->max_char_high;
            }
            result=i;
        }
        xfree(ranges);
    }
    return result;
}

/* *face_number and *spacing are initialized but *load_flags is NOT. */
static int 
FreeTypeSetUpTTCap( char *fileName, FontScalablePtr vals,
		    char **dynStrRealFileName, char **dynStrFTFileName,
		    struct TTCapInfo *ret, int *face_number, FT_Int32 *load_flags,
		    int *spacing, Bool *font_properties, char **dynStrTTCapCodeRange )
{
    int result = Successful;
    SDynPropRecValList listPropRecVal;
    SPropRecValContainer contRecValue;
    Bool hinting=True;
    Bool isEmbeddedBitmap = True;
    Bool alwaysEmbeddedBitmap = False;
    int pixel = vals->pixel;

    *font_properties=True;
    *dynStrRealFileName=NULL;
    *dynStrFTFileName=NULL;
    *dynStrTTCapCodeRange=NULL;

    if (SPropRecValList_new(&listPropRecVal)) {
        return AllocError;
    }

    {
        int len = strlen(fileName);
        char *capHead = NULL;
        {
            /* font cap */
            char *p1=NULL, *p2=NULL;
	    
	    p1=strrchr(fileName, '/');
	    if ( p1 == NULL ) p1 = fileName;
	    else p1++;
	    if (NULL != (p2=strrchr(p1, ':'))) {
		/* colon exist in the right side of slash. */
		int dirLen = p1-fileName;
		int baseLen = fileName+len - p2 -1;
		
		*dynStrRealFileName = (char *)xalloc(dirLen+baseLen+1);
		if( *dynStrRealFileName == NULL ) {
		    result = AllocError;
		    goto quit;
		}
		if ( 0 < dirLen )
		    memcpy(*dynStrRealFileName, fileName, dirLen);
		strcpy(*dynStrRealFileName+dirLen, p2+1);
		capHead = p1;
	    } else {
		*dynStrRealFileName = xstrdup(fileName);
		if( *dynStrRealFileName == NULL ) {
		    result = AllocError;
		    goto quit;
		}
	    }
        }

	/* font cap */
	if (capHead) {
	    if (SPropRecValList_add_by_font_cap(&listPropRecVal,
						capHead)) {
		result = BadFontPath;
		goto quit;
	    }
	}
    }

    *face_number=0;
    *spacing=0;
    ret->autoItalic=0.0;
    ret->scaleWidth=1.0;
    ret->scaleBBoxWidth = 1.0;
    ret->scaleBBoxHeight = 1.0;
    ret->doubleStrikeShift = 1;
    ret->adjustBBoxWidthByPixel = 0;
    ret->adjustLeftSideBearingByPixel = 0;
    ret->adjustRightSideBearingByPixel = 0;
    ret->flags = 0;
    ret->scaleBitmap = 0.0;
    ret->forceConstantSpacingBegin = -1;
    ret->forceConstantSpacingEnd = -1;
    ret->force_c_representative_metrics_char_code = -2;
    ret->force_c_scale_b_box_width = 1.0;
    ret->force_c_scale_b_box_height = 1.0;
    ret->force_c_adjust_width_by_pixel = 0;
    ret->force_c_adjust_lsb_by_pixel = 0;
    ret->force_c_adjust_rsb_by_pixel = 0;
    ret->force_c_scale_lsb = 0.0;
    ret->force_c_scale_rsb = 1.0;
    /* */
    ret->vl_slant=0;
    ret->lsbShiftOfBitmapAutoItalic=0;
    ret->rsbShiftOfBitmapAutoItalic=0;
    /* face number */
    {
	char *beginptr=NULL,*endptr;
	if ( SPropRecValList_search_record(&listPropRecVal,
                                       &contRecValue,
                                       "FaceNumber")) {
	    int lv;
	    beginptr = SPropContainer_value_str(contRecValue);
	    lv=strtol(beginptr, &endptr, 10);
	    if ( *beginptr != '\0' && *endptr == '\0' ) {
		if ( 0 < lv ) *face_number = lv;
	    }
	}
	if( beginptr && 0 < *face_number ) {
	    char *slash;
	    *dynStrFTFileName = 	/* add ->  ':'+strlen0+':'+strlen1+'\0' */
		(char *)xalloc(1+strlen(beginptr)+1+strlen(*dynStrRealFileName)+1);
	    if( *dynStrFTFileName == NULL ){
		result = AllocError;
		goto quit;
	    }
	    **dynStrFTFileName = '\0';
	    slash = strrchr(*dynStrRealFileName,'/');
	    if( slash ) {
		char *p;
		strcat(*dynStrFTFileName,*dynStrRealFileName);
		p = strrchr(*dynStrFTFileName,'/');
		p[1] = '\0';
		strcat(*dynStrFTFileName,":");
		strcat(*dynStrFTFileName,beginptr);
		strcat(*dynStrFTFileName,":");
		strcat(*dynStrFTFileName,slash+1);
	    }
	    else{
		strcat(*dynStrFTFileName,":");
		strcat(*dynStrFTFileName,beginptr);
		strcat(*dynStrFTFileName,":");
		strcat(*dynStrFTFileName,*dynStrRealFileName);
	    }
	}
	else{
	    *dynStrFTFileName = (char *)xalloc(strlen(*dynStrRealFileName)+1);
	    if( *dynStrFTFileName == NULL ){
		result = AllocError;
		goto quit;
	    }
	    **dynStrFTFileName = '\0';
	    strcat(*dynStrFTFileName,*dynStrRealFileName);
	}
    }
    /*
    fprintf(stderr,"[Filename:%s]\n",fileName);
    fprintf(stderr,"[RealFilename:%s]\n",*dynStrRealFileName);
    fprintf(stderr,"[FTFilename:%s]\n",*dynStrFTFileName);
    */    
    /* slant control */
    if (SPropRecValList_search_record(&listPropRecVal,
                                      &contRecValue,
                                      "AutoItalic"))
        ret->autoItalic = SPropContainer_value_dbl(contRecValue);
    /* hinting control */
    if (SPropRecValList_search_record(&listPropRecVal,
                                      &contRecValue,
                                      "Hinting"))
        hinting = SPropContainer_value_bool(contRecValue);
    /* scaling */
    if (SPropRecValList_search_record(&listPropRecVal,
                                      &contRecValue,
                                      "ScaleWidth")) {
        ret->scaleWidth = SPropContainer_value_dbl(contRecValue);
        if (ret->scaleWidth<=0.0) {
            fprintf(stderr, "ScaleWitdh needs plus.\n");
	    result = BadFontName;
	    goto quit;
	}
    }
    /* bbox adjustment */
    if (SPropRecValList_search_record(&listPropRecVal,
                                      &contRecValue,
                                      "ScaleBBoxWidth")) {
        /* Scaling to Bounding Box Width */
        int lv;
        char *endptr,*beginptr;
        double v,scaleBBoxWidth=1.0,scaleBBoxHeight=1.0;
        beginptr = SPropContainer_value_str(contRecValue);
        do {
            if ( strlen(beginptr) < 1 ) break;
            v=strtod(beginptr, &endptr);
            if ( endptr!=beginptr ) {
                scaleBBoxWidth = v;
            }
            if ( *endptr != ';' && *endptr != ',' ) break;
	    if ( *endptr == ',' ) {
		beginptr=endptr+1;
		v=strtod(beginptr, &endptr);
		if ( endptr!=beginptr ) {
		    scaleBBoxHeight = v;
		}
	    }
            if ( *endptr != ';' && *endptr != ',' ) break;
            beginptr=endptr+1;
            lv=strtol(beginptr, &endptr, 10);
            if ( endptr!=beginptr ) {
                ret->adjustBBoxWidthByPixel = lv;
            }
            if ( *endptr != ',' ) break;
            beginptr=endptr+1;
            lv=strtol(beginptr, &endptr, 10);
            if ( endptr!=beginptr ) {
                ret->adjustLeftSideBearingByPixel = lv;
            }
            if ( *endptr != ',' ) break;
            beginptr=endptr+1;
            lv=strtol(beginptr, &endptr, 10);
            if ( endptr!=beginptr ) {
                ret->adjustRightSideBearingByPixel = lv;
            }
        } while ( 0 );
        if (scaleBBoxWidth<=0.0) {
            fprintf(stderr, "ScaleBBoxWitdh needs plus.\n");
	    result = BadFontName;
	    goto quit;
        }
        if (scaleBBoxHeight<=0.0) {
            fprintf(stderr, "ScaleBBoxHeight needs plus.\n");
	    result = BadFontName;
	    goto quit;
        }
        ret->scaleBBoxWidth  = scaleBBoxWidth;
        ret->scaleBBoxHeight = scaleBBoxHeight;
    }
    /* spacing */
    if (SPropRecValList_search_record(&listPropRecVal,
				      &contRecValue,
				      "ForceSpacing")) {
	char *strSpace = SPropContainer_value_str(contRecValue);
	Bool err = False;
	if (1 != strlen(strSpace))
	    err = True;
	else
	    switch (strSpace[0]) {
	    case 'M':
		ret->flags |= TTCAP_MONO_CENTER;
		*spacing = 'm';
		break;
	    case 'm':
	    case 'p':
	    case 'c':
		*spacing = strSpace[0];
		break;
	    default:
		err = True;
	    }
	if (err) {
	    result = BadFontName;
	    goto quit;
	}
    }
    /* doube striking */
    if (SPropRecValList_search_record(&listPropRecVal,
                                      &contRecValue,
                                      "DoubleStrike")) {
        /* Set or Reset Auto Bold Flag */
        char *strDoubleStrike = SPropContainer_value_str(contRecValue);
        Bool err = False;
        if ( 0 < strlen(strDoubleStrike) ) {
            switch (strDoubleStrike[0]) {
            case 'm':
            case 'M':
            case 'l':
            case 'L':
                ret->flags |= TTCAP_DOUBLE_STRIKE;
                ret->flags |= TTCAP_DOUBLE_STRIKE_MKBOLD_EDGE_LEFT;
                break;
            case 'y':
            case 'Y':
                ret->flags |= TTCAP_DOUBLE_STRIKE;
                break;
            case 'n':
            case 'N':
                ret->flags &= ~TTCAP_DOUBLE_STRIKE;
                ret->flags &= ~TTCAP_DOUBLE_STRIKE_MKBOLD_EDGE_LEFT;
                ret->flags &= ~TTCAP_DOUBLE_STRIKE_CORRECT_B_BOX_WIDTH;
                break;
            default:
                err = True;
            }
            if ( err != True ) {
                if ( strDoubleStrike[1] ) {
                    switch (strDoubleStrike[1]) {
                    case 'b':
                    case 'B':
                    case 'p':
                    case 'P':
                    case 'y':
                    case 'Y':
                        ret->flags |= TTCAP_DOUBLE_STRIKE_CORRECT_B_BOX_WIDTH;
                        break;
                    default:
                        break;
                    }
                }
                do {
                    char *comma_ptr=strchr(strDoubleStrike,';');
                    if ( !comma_ptr ) comma_ptr=strchr(strDoubleStrike,',');
                    if ( !comma_ptr ) break;
		    if ( comma_ptr[1] ) {
			char *endptr;
			int mkboldMaxPixel;
			mkboldMaxPixel=strtol(comma_ptr+1, &endptr, 10);
			if ( endptr != comma_ptr+1 && mkboldMaxPixel <= pixel ) {
			    ret->flags &= ~TTCAP_DOUBLE_STRIKE_MKBOLD_EDGE_LEFT;
			}
		    }
		    comma_ptr=strchr(comma_ptr+1,',');
		    if ( !comma_ptr ) break;
		    if ( comma_ptr[1] ) {
			char *endptr;
			int max_pixel;
			max_pixel=strtol(comma_ptr+1, &endptr, 10);
			if ( endptr != comma_ptr+1 && max_pixel <= pixel ) {
			  if( ret->flags & TTCAP_DOUBLE_STRIKE )
			    ret->doubleStrikeShift += pixel / max_pixel;
			}
		    }
                } while(0);
            }
        }
        else
            err = True;
        if (err) {
            result = BadFontName;
            goto quit;
        }
    }
    /* very lazy metrics */
    if (SPropRecValList_search_record(&listPropRecVal,
                                      &contRecValue,
                                      "VeryLazyMetrics")){
	Bool isVeryLazy = SPropContainer_value_bool(contRecValue);
	ret->flags |= TTCAP_DISABLE_DEFAULT_VERY_LAZY;
	if( isVeryLazy == True )
	    ret->flags |= TTCAP_IS_VERY_LAZY;
	else
	    ret->flags &= ~TTCAP_IS_VERY_LAZY;
    }
    /* embedded bitmap */
    if (SPropRecValList_search_record(&listPropRecVal,
                                      &contRecValue,
                                      "EmbeddedBitmap")) {
        char *strEmbeddedBitmap = SPropContainer_value_str(contRecValue);
        Bool err = False;
        if ( 1 == strlen(strEmbeddedBitmap) ) {
            switch (strEmbeddedBitmap[0]) {
            case 'y':
            case 'Y':
                isEmbeddedBitmap = True;
                alwaysEmbeddedBitmap = True;
                break;
            case 'u':
            case 'U':
                isEmbeddedBitmap = True;
                alwaysEmbeddedBitmap = False;
                break;
            case 'n':
            case 'N':
                isEmbeddedBitmap = False;
                break;
            default:
                err = True;
            }
        }
        else
            err = True;
        if (err) {
            result = BadFontName;
            goto quit;
        }
    }
    /* scale bitmap */
    if((ret->flags & TTCAP_IS_VERY_LAZY) &&
       SPropRecValList_search_record(&listPropRecVal,
                                     &contRecValue,
                                     "VeryLazyBitmapWidthScale")) {
        /* Scaling to Bitmap Bounding Box Width */
        double scaleBitmapWidth = SPropContainer_value_dbl(contRecValue);

	fprintf(stderr, "Warning: `bs' option is not required in X-TT version 2.\n");
#if 0
        if (scaleBitmapWidth<=0.0) {
            fprintf(stderr, "ScaleBitmapWitdh needs plus.\n");
            result = BadFontName;
            goto quit;
        }
#endif
        ret->scaleBitmap = scaleBitmapWidth;
    }
    /* restriction of the code range */
    if (SPropRecValList_search_record(&listPropRecVal,
				      &contRecValue,
				      "CodeRange")) {
	*dynStrTTCapCodeRange = xstrdup(SPropContainer_value_str(contRecValue));
	if( *dynStrTTCapCodeRange == NULL ) {
	    result = AllocError;
	    goto quit;
	}
    }
    /* forceConstantSpacing{Begin,End} */
    if ( 1 /* ft->spacing == 'p' */ ){
        unsigned short first_col=0,last_col=0x00ff;
        unsigned short first_row=0,last_row=0x00ff;
        if (SPropRecValList_search_record(&listPropRecVal,
                                           &contRecValue,
                                           "ForceConstantSpacingCodeRange")) {
            if ( restrict_code_range_by_str(1,&first_col, &first_row,
                                            &last_col, &last_row,
                                            SPropContainer_value_str(contRecValue)) == 1 ) {
              ret->forceConstantSpacingBegin = (int)( first_row<<8 | first_col );
              ret->forceConstantSpacingEnd = (int)( last_row<<8 | last_col );
	      if ( ret->forceConstantSpacingBegin <= ret->forceConstantSpacingEnd )
		  ret->flags &= ~TTCAP_FORCE_C_OUTSIDE;
	      else ret->flags |= TTCAP_FORCE_C_OUTSIDE;
            }
        }
    }
    /* */
    if ( 1 ){
        unsigned short first_col=0, last_col=0x0ff;
        unsigned short first_row=0, last_row=0x0ff;
        if ( SPropRecValList_search_record(&listPropRecVal,
                                           &contRecValue,
                                           "ForceConstantSpacingMetrics")) {
            char *strMetrics;
            strMetrics = SPropContainer_value_str(contRecValue);
            if ( strMetrics ) {
                char *comma_ptr,*period_ptr,*semic_ptr;
                semic_ptr=strchr(strMetrics,';');
                comma_ptr=strchr(strMetrics,',');
                period_ptr=strchr(strMetrics,'.');
                if ( semic_ptr && comma_ptr ) 
                    if ( semic_ptr < comma_ptr ) comma_ptr=NULL;
                if ( semic_ptr && period_ptr ) 
                    if ( semic_ptr < period_ptr ) period_ptr=NULL;
                if ( !comma_ptr && !period_ptr && strMetrics != semic_ptr ) {
                    if ( restrict_code_range_by_str(1,&first_col, &first_row,
                                                    &last_col, &last_row,
                                                    SPropContainer_value_str(contRecValue)) == 1 ) {
                      ret->force_c_representative_metrics_char_code = 
                          (int)( first_row<<8 | first_col );
                    }
                }
                else {
                    double v;
                    char *endptr,*beginptr=strMetrics;
                    do {
                        v=strtod(beginptr, &endptr);
                        if ( endptr!=beginptr ) {
                            ret->force_c_scale_b_box_width = v;
                        }
                        if ( *endptr != ',' ) break;
                        beginptr=endptr+1;
                        v=strtod(beginptr, &endptr);
                        if ( endptr!=beginptr ) {
                            ret->force_c_scale_lsb = v;
                            ret->flags |= TTCAP_FORCE_C_LSB_FLAG;
                        }
                        if ( *endptr != ',' ) break;
                        beginptr=endptr+1;
                        v=strtod(beginptr, &endptr);
                        if ( endptr!=beginptr ) {
                            ret->force_c_scale_rsb = v;
                            ret->flags |= TTCAP_FORCE_C_RSB_FLAG;
                        }
                        if ( *endptr != ',' ) break;
                        beginptr=endptr+1;
                        v=strtod(beginptr, &endptr);
                        if ( endptr!=beginptr ) {
                            ret->force_c_scale_b_box_height = v;
			}
                    } while (0);
                }
                if ( semic_ptr ) {
                    int lv;
                    char *endptr,*beginptr=semic_ptr+1;
                    do {
                        lv=strtol(beginptr, &endptr, 10);
                        if ( endptr!=beginptr ) {
                            ret->force_c_adjust_width_by_pixel=lv;
                        }
                        if ( *endptr != ',' ) break;
                        beginptr=endptr+1;
                        lv=strtol(beginptr, &endptr, 10);
                        if ( endptr!=beginptr ) {
                            ret->force_c_adjust_lsb_by_pixel=lv;
                        }
                        if ( *endptr != ',' ) break;
                        beginptr=endptr+1;
                        lv=strtol(beginptr, &endptr, 10);
                        if ( endptr!=beginptr ) {
                            ret->force_c_adjust_rsb_by_pixel=lv;
                        }
                    } while (0);
                }
            }
        }
    }

    if (SPropRecValList_search_record(&listPropRecVal,
                                      &contRecValue,
                                      "FontProperties")) {
        /* Set or Reset the Flag of FontProperties */
        *font_properties=SPropContainer_value_bool(contRecValue);
    }

    ret->force_c_scale_b_box_width *= ret->scaleBBoxWidth;
    ret->force_c_scale_b_box_height *= ret->scaleBBoxHeight;

    ret->force_c_scale_b_box_width *= ret->scaleWidth;
    ret->scaleBBoxWidth            *= ret->scaleWidth;

    ret->force_c_adjust_rsb_by_pixel += ret->adjustRightSideBearingByPixel;
    ret->force_c_adjust_lsb_by_pixel += ret->adjustLeftSideBearingByPixel;

    /* scaleWidth, scaleBBoxWidth, force_c_scale_b_box_width, force_c_scale_b_box_width */

    /* by TTCap */
    if( hinting == False ) *load_flags |= FT_LOAD_NO_HINTING;
    if( isEmbeddedBitmap == False ) *load_flags |= FT_LOAD_NO_BITMAP;
    if( ret->autoItalic != 0 && alwaysEmbeddedBitmap == False )
	*load_flags |= FT_LOAD_NO_BITMAP;

 quit:
    return result;
}

static int
ft_get_trans_from_vals( FontScalablePtr vals, FTNormalisedTransformationPtr trans )
{
    /* Compute the transformation matrix.  We use floating-point
       arithmetic for simplicity */
    
    trans->xres = vals->x;
    trans->yres = vals->y;
    
    /* This value cannot be 0. */
    trans->scale = hypot(vals->point_matrix[2], vals->point_matrix[3]);
    trans->nonIdentity = 0;
    
    /* Try to round stuff.  We want approximate zeros to be exact zeros,
       and if the elements on the diagonal are approximately equal, we
       want them equal.  We do this to avoid breaking hinting. */
    if(DIFFER(vals->point_matrix[0], vals->point_matrix[3])) {
        trans->nonIdentity = 1;
        trans->matrix.xx =
            (int)((vals->point_matrix[0]*(double)TWO_SIXTEENTH)/trans->scale);
        trans->matrix.yy =
            (int)((vals->point_matrix[3]*(double)TWO_SIXTEENTH)/trans->scale);
    } else {
        trans->matrix.xx = trans->matrix.yy =
            ((vals->point_matrix[0] + vals->point_matrix[3])/2*
             (double)TWO_SIXTEENTH)/trans->scale;
    }
    
    if(DIFFER0(vals->point_matrix[1], trans->scale)) {
        trans->matrix.yx =
            (int)((vals->point_matrix[1]*(double)TWO_SIXTEENTH)/trans->scale);
        trans->nonIdentity = 1;
    } else
        trans->matrix.yx = 0;
    
    if(DIFFER0(vals->point_matrix[2], trans->scale)) {
        trans->matrix.xy =
            (int)((vals->point_matrix[2]*(double)TWO_SIXTEENTH)/trans->scale);
        trans->nonIdentity = 1;
    } else
        trans->matrix.xy=0;
    return 0;
}


static int
is_fixed_width(FT_Face face)
{
    PS_FontInfoRec t1info_rec;
    int ftrc;

    if(FT_IS_FIXED_WIDTH(face)) {
        return 1;
    }

    ftrc = FT_Get_PS_Font_Info(face, &t1info_rec);
    if(ftrc == 0 && t1info_rec.is_fixed_pitch) {
        return 1;
    }

    return 0;
}

static int
FreeTypeLoadFont(FTFontPtr font, FontInfoPtr info, FTFacePtr face,
		 char *FTFileName, FontScalablePtr vals, FontEntryPtr entry,
                 FontBitmapFormatPtr bmfmt, FT_Int32 load_flags, 
		 struct TTCapInfo *tmp_ttcap, char *dynStrTTCapCodeRange,
		 int ttcap_spacing )
{
    int xrc;
    FTNormalisedTransformationRec trans;
    int spacing, actual_spacing, zero_code;
    long  lastCode, firstCode;
    TT_Postscript *post;

    ft_get_trans_from_vals(vals,&trans);

    /* Check for charcell in XLFD */
    spacing = FT_PROPORTIONAL;
    if(entry->name.ndashes == 14) {
        char *p;
        int dashes = 0;
        for(p = entry->name.name; 
            p <= entry->name.name + entry->name.length - 2;
            p++) {
            if(*p == '-') {
                dashes++;
                if(dashes == 11) {
                    if(p[1]=='c' && p[2]=='-')
                        spacing=FT_CHARCELL;
		    else if(p[1]=='m' && p[2]=='-')
                        spacing=FT_MONOSPACED;
                    break;
                }
            }
        }
    }
    /* by TTCap  */
    if( ttcap_spacing != 0 ) {
	if( ttcap_spacing == 'c' ) spacing=FT_CHARCELL;
	else if( ttcap_spacing == 'm' ) spacing=FT_MONOSPACED;
	else spacing=FT_PROPORTIONAL;
    }

    actual_spacing = spacing;
    if( spacing == FT_PROPORTIONAL ) {
	if( is_fixed_width(face->face) )
	    actual_spacing = FT_MONOSPACED;
    }

    if(entry->name.ndashes == 14) {
        xrc = FTPickMapping(entry->name.name, entry->name.length, FTFileName,
			    face->face, &font->mapping);
	if (xrc != Successful)
	    return xrc;
    } else {
        xrc = FTPickMapping(0, 0, FTFileName, 
			    face->face, &font->mapping);
	if (xrc != Successful)
	    return xrc;
    }
    
    font->nranges = vals->nranges;
    font->ranges = 0;
    if(font->nranges) {
        font->ranges = (fsRange*)xalloc(vals->nranges*sizeof(fsRange));
        if(font->ranges == NULL) 
            return AllocError;
        memcpy((char*)font->ranges, (char*)vals->ranges,
               vals->nranges*sizeof(fsRange));
    }

    zero_code=-1;
    if(info) {
        firstCode = 0;
        lastCode = 0xFFFFL;
        if(!font->mapping.mapping || 
           font->mapping.mapping->encoding->row_size == 0) {
            /* linear indexing */
            lastCode=MIN(lastCode,
                         font->mapping.mapping ?
                         font->mapping.mapping->encoding->size-1 :
                         0xFF);
            if(font->mapping.mapping && font->mapping.mapping->encoding->first)
                firstCode = font->mapping.mapping->encoding->first;
            info->firstRow = firstCode/0x100;
            info->lastRow = lastCode/0x100;
            info->firstCol =
                (info->firstRow || info->lastRow) ? 0 : (firstCode & 0xFF);
            info->lastCol = info->lastRow ? 0xFF : (lastCode & 0xFF);
	    if ( firstCode == 0 ) zero_code=0;
        } else {
            /* matrix indexing */
            info->firstRow = font->mapping.mapping->encoding->first;
            info->lastRow = MIN(font->mapping.mapping->encoding->size-1,
                                lastCode/0x100);
            info->firstCol = font->mapping.mapping->encoding->first_col;
            info->lastCol = MIN(font->mapping.mapping->encoding->row_size-1, 
                                lastCode<0x100?lastCode:0xFF);
	    if( info->firstRow == 0 && info->firstCol == 0 ) zero_code=0;
        }

        /* firstCode and lastCode are not valid in case of a matrix
           encoding */

	if( dynStrTTCapCodeRange ) {
	    restrict_code_range_by_str(0,&info->firstCol, &info->firstRow,
				       &info->lastCol, &info->lastRow,
				       dynStrTTCapCodeRange);
	}
	restrict_code_range(&info->firstCol, &info->firstRow,
			    &info->lastCol, &info->lastRow,
			    font->ranges, font->nranges);
    }
    font->info = info;

    /* zero code is frequently used. */
    if ( zero_code < 0 ) {
	/* The fontenc should have the information of DefaultCh.
	   But we do not have such a information.
	   So we cannot but set 0. */
	font->zero_idx = 0;
    }
    else
	font->zero_idx = FTRemap(face->face, 
				 &font->mapping, zero_code);

    post = FT_Get_Sfnt_Table(face->face, ft_sfnt_post);

#ifdef DEFAULT_VERY_LAZY
    if( !( tmp_ttcap->flags & TTCAP_DISABLE_DEFAULT_VERY_LAZY ) )
	if( DEFAULT_VERY_LAZY <= 1 + info->lastRow - info->firstRow ) {
	    if( post ){
		tmp_ttcap->flags |= TTCAP_IS_VERY_LAZY;
	    }
	}
#endif
    /* We should always reset. */
    tmp_ttcap->flags &= ~TTCAP_DISABLE_DEFAULT_VERY_LAZY;

    if ( face->bitmap || actual_spacing == FT_CHARCELL )
	tmp_ttcap->flags &= ~TTCAP_IS_VERY_LAZY;
    /* "vl=y" is available when TrueType or OpenType only */
    if ( !face->bitmap && !(FT_IS_SFNT( face->face )) )
	tmp_ttcap->flags &= ~TTCAP_IS_VERY_LAZY;

    if( post ) {
	if( post->italicAngle != 0 )
	    tmp_ttcap->vl_slant = -sin( (post->italicAngle/1024./5760.)*1.57079632679489661923 );
	/* fprintf(stderr,"angle=%g(%g)\n",tmp_ttcap->vl_slant,(post->italicAngle/1024./5760.)*90); */
    }

    xrc = FreeTypeOpenInstance(&font->instance, face,
                               FTFileName, &trans, actual_spacing, bmfmt,
			       tmp_ttcap, load_flags );
    return xrc;
}

static void
adjust_min_max(xCharInfo *minc, xCharInfo *maxc, xCharInfo *tmp)
{
#define MINMAX(field,ci) \
    if (minc->field > (ci)->field) \
    minc->field = (ci)->field; \
    if (maxc->field < (ci)->field) \
    maxc->field = (ci)->field;

    MINMAX(ascent, tmp);
    MINMAX(descent, tmp);
    MINMAX(leftSideBearing, tmp);
    MINMAX(rightSideBearing, tmp);
    MINMAX(characterWidth, tmp);

    if ((INT16)minc->attributes > (INT16)tmp->attributes)
        minc->attributes = tmp->attributes;
    if ((INT16)maxc->attributes < (INT16)tmp->attributes)
        maxc->attributes = tmp->attributes;
#undef  MINMAX
}

static void
ft_compute_bounds(FTFontPtr font, FontInfoPtr pinfo, FontScalablePtr vals )
{
    FTInstancePtr instance;
    int row, col;
    unsigned int c;
    xCharInfo minchar, maxchar, *tmpchar = NULL;
    int overlap, maxOverlap;
    long swidth      = 0;
    long total_width = 0;
    int num_cols, num_chars = 0;
    int flags, skip_ok = 0;
    int force_c_outside ;

    instance = font->instance;
    force_c_outside = instance->ttcap.flags & TTCAP_FORCE_C_OUTSIDE;

    minchar.ascent = minchar.descent =
    minchar.leftSideBearing = minchar.rightSideBearing =
    minchar.characterWidth = minchar.attributes = 32767;
    maxchar.ascent = maxchar.descent =
    maxchar.leftSideBearing = maxchar.rightSideBearing =
    maxchar.characterWidth = maxchar.attributes = -32767;
    maxOverlap = -32767;

    /* Parse all glyphs */
    num_cols = 1 + pinfo->lastCol - pinfo->firstCol;
    for (row = pinfo->firstRow; row <= pinfo->lastRow; row++) {
      if ( skip_ok && tmpchar ) {
        if ( !force_c_outside ) {
          if ( instance->ttcap.forceConstantSpacingBegin < row<<8 
	       && row<<8 < (instance->ttcap.forceConstantSpacingEnd & 0x0ff00) ) {
            if (tmpchar->characterWidth) {
              num_chars += num_cols;
              swidth += ABS(tmpchar->characterWidth)*num_cols;
              total_width += tmpchar->characterWidth*num_cols;
              continue;
            }
          }
          else skip_ok=0;
        }
        else {          /* for GB18030 proportional */
          if ( instance->ttcap.forceConstantSpacingBegin < row<<8 
	       || row<<8 < (instance->ttcap.forceConstantSpacingEnd & 0x0ff00) ) {
            if (tmpchar->characterWidth) {
              num_chars += num_cols;
              swidth += ABS(tmpchar->characterWidth)*num_cols;
              total_width += tmpchar->characterWidth*num_cols;
              continue;
            }
          }
          else skip_ok=0;
        }
      }
      for (col = pinfo->firstCol; col <= pinfo->lastCol; col++) {
          c = row<<8|col;
          flags=0;
          if ( !force_c_outside ) {
              if ( c <= instance->ttcap.forceConstantSpacingEnd 
		   && instance->ttcap.forceConstantSpacingBegin <= c )
                  flags|=FT_FORCE_CONSTANT_SPACING;
          }
          else {        /* for GB18030 proportional */
              if ( c <= instance->ttcap.forceConstantSpacingEnd 
		   || instance->ttcap.forceConstantSpacingBegin <= c )
                  flags|=FT_FORCE_CONSTANT_SPACING;
          }
#if 0
          fprintf(stderr, "comp_bounds: %x ->", c);
#endif
          if ( skip_ok == 0 || flags == 0 ){
              tmpchar=NULL;
#if 0
              fprintf(stderr, "%x\n", c);
#endif
	      if( FreeTypeFontGetGlyphMetrics(c, flags, &tmpchar, font) != Successful )
		  continue;
          }
          if ( !tmpchar ) continue;
          adjust_min_max(&minchar, &maxchar, tmpchar);
          overlap = tmpchar->rightSideBearing - tmpchar->characterWidth;
          if (maxOverlap < overlap)
              maxOverlap = overlap;
          
          if (!tmpchar->characterWidth)
              continue;
          num_chars++;
          swidth += ABS(tmpchar->characterWidth);
          total_width += tmpchar->characterWidth;
          
          if ( flags & FT_FORCE_CONSTANT_SPACING ) skip_ok=1;
      }
    }

#ifndef X_ACCEPTS_NO_SUCH_CHAR
    /* Check code 0 */
    if( FreeTypeInstanceGetGlyphMetrics(font->zero_idx, 0, &tmpchar, font->instance) != Successful || tmpchar == NULL)
	if( FreeTypeInstanceGetGlyphMetrics(font->zero_idx, FT_GET_DUMMY, &tmpchar, font->instance) != Successful )
	    tmpchar = NULL;
    if ( tmpchar ) {
	adjust_min_max(&minchar, &maxchar, tmpchar);
	overlap = tmpchar->rightSideBearing - tmpchar->characterWidth;
	if (maxOverlap < overlap)
	    maxOverlap = overlap;
    }
#endif

    /* AVERAGE_WIDTH ... 1/10 pixel unit */
    if (num_chars > 0) {
        swidth = (swidth * 10.0 + num_chars / 2.0) / num_chars;
        if (total_width < 0)
            swidth = -swidth;
        vals->width = swidth;
    } else
        vals->width = 0;

    /*
    if (char_width.pixel) {
        maxchar.characterWidth = char_width.pixel;
        minchar.characterWidth = char_width.pixel;
    }
    */

    pinfo->maxbounds     = maxchar;
    pinfo->minbounds     = minchar;
    pinfo->ink_maxbounds = maxchar;
    pinfo->ink_minbounds = minchar;
    pinfo->maxOverlap    = maxOverlap;
}

static int
compute_new_extents( FontScalablePtr vals, double scale, double lsb, double rsb, double desc, double asc,
		     int *lsb_result, int *rsb_result, int *desc_result, int *asc_result )
{
#define TRANSFORM_POINT(matrix, x, y, dest) \
    ((dest)[0] = (matrix)[0] * (x) + (matrix)[2] * (y), \
     (dest)[1] = (matrix)[1] * (x) + (matrix)[3] * (y))

#define CHECK_EXTENT(lsb, rsb, desc, asc, data) \
    ((lsb) > (data)[0] ? (lsb) = (data)[0] : 0 , \
     (rsb) < (data)[0] ? (rsb) = (data)[0] : 0, \
     (-desc) > (data)[1] ? (desc) = -(data)[1] : 0 , \
     (asc) < (data)[1] ? (asc) = (data)[1] : 0)
    double newlsb, newrsb, newdesc, newasc;
    double point[2];

    /* Compute new extents for this glyph */
    TRANSFORM_POINT(vals->pixel_matrix, lsb, -desc, point);
    newlsb  = point[0];
    newrsb  = newlsb;
    newdesc = -point[1];
    newasc  = -newdesc;
    TRANSFORM_POINT(vals->pixel_matrix, lsb, asc, point);
    CHECK_EXTENT(newlsb, newrsb, newdesc, newasc, point);
    TRANSFORM_POINT(vals->pixel_matrix, rsb, -desc, point);
    CHECK_EXTENT(newlsb, newrsb, newdesc, newasc, point);
    TRANSFORM_POINT(vals->pixel_matrix, rsb, asc, point);
    CHECK_EXTENT(newlsb, newrsb, newdesc, newasc, point);

    /* ???: lsb = (int)floor(newlsb * scale); */
    *lsb_result   = (int)floor(newlsb * scale + 0.5);
    *rsb_result   = (int)floor(newrsb * scale + 0.5);
    *desc_result  = (int)ceil(newdesc * scale - 0.5);
    *asc_result   = (int)floor(newasc * scale + 0.5);

    return 0;
#undef CHECK_EXTENT
#undef TRANSFORM_POINT
}

static int
is_matrix_unit(FontScalablePtr vals)
{
    double base_size;
    FT_Matrix m;

    base_size = hypot(vals->point_matrix[2], vals->point_matrix[3]);

    m.xx = vals->point_matrix[0] / base_size * 65536;
    m.xy = vals->point_matrix[2] / base_size * 65536;
    m.yx = vals->point_matrix[1] / base_size * 65536;
    m.yy = vals->point_matrix[3] / base_size * 65536;

    return (m.xx == 65536) && (m.yx == 0) && 
	   (m.xy == 0) && (m.yy == 65536);
}

/* Do all the real work for OpenFont or FontInfo */
/* xf->info is only accessed through info, and xf might be null */

static int
FreeTypeLoadXFont(char *fileName, 
                  FontScalablePtr vals, FontPtr xf, FontInfoPtr info,
                  FontBitmapFormatPtr bmfmt, FontEntryPtr entry)
{
    FTFontPtr font = NULL;
    FTFacePtr face = NULL;
    FTInstancePtr instance;
    FT_Size_Metrics *smetrics;
    int xrc=Successful;
    int charcell;
    long rawWidth = 0, rawAverageWidth = 0;
    int upm, minLsb, maxRsb, ascent, descent, width, averageWidth;
    double scale, base_width, base_height;
    Bool orig_is_matrix_unit, font_properties;
    int face_number, ttcap_spacing;
    struct TTCapInfo tmp_ttcap;
    struct TTCapInfo *ins_ttcap;
    FT_Int32 load_flags = FT_LOAD_DEFAULT;	/* orig: FT_LOAD_RENDER | FT_LOAD_MONOCHROME */
    char *dynStrRealFileName   = NULL;	/* foo.ttc */
    char *dynStrFTFileName     = NULL;	/* :1:foo.ttc */
    char *dynStrTTCapCodeRange = NULL;

    font = (FTFontPtr)xalloc(sizeof(FTFontRec));
    if(font == NULL) {
        xrc = AllocError;
        goto quit;
    }
    memset(font, 0, sizeof(FTFontRec));

    xrc = FreeTypeSetUpTTCap(fileName, vals, 
			     &dynStrRealFileName, &dynStrFTFileName,
			     &tmp_ttcap, &face_number, 
			     &load_flags, &ttcap_spacing,
			     &font_properties, &dynStrTTCapCodeRange);
    if ( xrc != Successful ) {
	goto quit;
    }

    xrc = FreeTypeOpenFace(&face, dynStrFTFileName, dynStrRealFileName, face_number);
    if(xrc != Successful) {
        goto quit;
    }

    if( is_matrix_unit(vals) )
	orig_is_matrix_unit = True;
    else {
	orig_is_matrix_unit = False;
	/* Turn off EmbeddedBitmap when original matrix is not diagonal. */
	load_flags |= FT_LOAD_NO_BITMAP;
    }

    if( face->bitmap ) load_flags &= ~FT_LOAD_NO_BITMAP;

    /* Slant control by TTCap */
    if(!face->bitmap) {
	vals->pixel_matrix[2] +=
	    vals->pixel_matrix[0] * tmp_ttcap.autoItalic;
	vals->point_matrix[2] +=
	    vals->point_matrix[0] * tmp_ttcap.autoItalic;
	vals->pixel_matrix[3] +=
	    vals->pixel_matrix[1] * tmp_ttcap.autoItalic;
	vals->point_matrix[3] +=
	    vals->point_matrix[1] * tmp_ttcap.autoItalic;
    }

    base_width=hypot(vals->pixel_matrix[0], vals->pixel_matrix[1]);
    base_height=hypot(vals->pixel_matrix[2], vals->pixel_matrix[3]);
    if(MAX(base_width, base_height) < 1.0 ) {
        xrc = BadFontName;
	goto quit;
    }

    xrc = FreeTypeLoadFont(font, info, face, dynStrFTFileName, vals, entry, bmfmt,
			   load_flags, &tmp_ttcap, dynStrTTCapCodeRange, 
			   ttcap_spacing );
    if(xrc != Successful) {
        goto quit;
    }

    instance = font->instance;
    smetrics = &instance->size->metrics;
    ins_ttcap = &instance->ttcap;

    upm = face->face->units_per_EM;
    if(upm == 0) {
        /* Work around FreeType bug */
        upm = WORK_AROUND_UPM;
    }
    scale = 1.0 / upm;

    charcell = (instance->spacing == FT_CHARCELL);

    if( instance->charcellMetrics == NULL ) {

	/* New instance */

	long force_c_rawWidth = 0;
	int force_c_lsb,force_c_rsb,force_c_width;
	double unit_x=0,unit_y=0,advance;
	CharInfoPtr tmpglyph;

	/*
	 * CALCULATE HEADER'S METRICS
	 */

	/* for OUTLINE fonts */
	if(!face->bitmap) {
	    int new_width;
	    double ratio,force_c_ratio;
	    double width_x=0,width_y=0;
	    double force_c_width_x, force_c_rsb_x, force_c_lsb_x;
	    double tmp_rsb,tmp_lsb,tmp_asc,tmp_des;
	    double max_advance_height;
	    tmp_asc = face->face->bbox.yMax;
	    tmp_des = -(face->face->bbox.yMin);
	    if ( tmp_asc < face->face->ascender ) tmp_asc = face->face->ascender;
	    if ( tmp_des < -(face->face->descender) ) tmp_des = -(face->face->descender);
	    tmp_lsb = face->face->bbox.xMin;
	    tmp_rsb = face->face->bbox.xMax;
	    if ( tmp_rsb < face->face->max_advance_width ) tmp_rsb = face->face->max_advance_width;
	    /* apply scaleBBoxWidth */
	    /* we should not ...??? */
	    tmp_lsb *= ins_ttcap->scaleBBoxWidth;
	    tmp_rsb *= ins_ttcap->scaleBBoxWidth;
	    /* transform and rescale */
	    compute_new_extents( vals, scale, tmp_lsb, tmp_rsb, tmp_des, tmp_asc,
				 &minLsb, &maxRsb, &descent, &ascent );
	    /* */
	    /* Consider vertical layouts */
	    if( 0 < face->face->max_advance_height )
		max_advance_height = face->face->max_advance_height;
	    else
		max_advance_height = tmp_asc + tmp_des;
	    if( vals->pixel_matrix[1] == 0 ){
		unit_x = fabs(vals->pixel_matrix[0]);
		unit_y = 0;
		width_x = face->face->max_advance_width * ins_ttcap->scaleBBoxWidth * unit_x;
	    }
	    else if( vals->pixel_matrix[3] == 0 ){
		unit_y = fabs(vals->pixel_matrix[2]);
		unit_x = 0;
		width_x = max_advance_height * ins_ttcap->scaleBBoxHeight * unit_y;
	    }
	    else{
		unit_x = fabs(vals->pixel_matrix[0] - 
			      vals->pixel_matrix[1]*vals->pixel_matrix[2]/vals->pixel_matrix[3]);
		unit_y = fabs(vals->pixel_matrix[2] - 
			      vals->pixel_matrix[3]*vals->pixel_matrix[0]/vals->pixel_matrix[1]);
		width_x = face->face->max_advance_width * ins_ttcap->scaleBBoxWidth * unit_x;
		width_y = max_advance_height * ins_ttcap->scaleBBoxHeight * unit_y;
		if( width_y < width_x ){
		    width_x = width_y;
		    unit_x = 0;
		}
		else{
		    unit_y = 0;
		}
	    }
	    /* calculate correction ratio */
	    width = (int)floor( (advance = width_x * scale) + 0.5);
	    new_width = width;
	    if( ins_ttcap->flags & TTCAP_DOUBLE_STRIKE_CORRECT_B_BOX_WIDTH )
		new_width += ins_ttcap->doubleStrikeShift;
	    new_width += ins_ttcap->adjustBBoxWidthByPixel;
	    ratio = (double)new_width/width;
	    width = new_width;
	    /* force constant */
	    if( unit_x != 0 ) {
		force_c_width_x = face->face->max_advance_width
		    * ins_ttcap->force_c_scale_b_box_width * unit_x;
		force_c_lsb_x = face->face->max_advance_width
		    * ins_ttcap->force_c_scale_lsb * unit_x;
		force_c_rsb_x = face->face->max_advance_width
		    * ins_ttcap->force_c_scale_rsb * unit_x;
	    }
	    else {
		force_c_width_x = max_advance_height
		    * ins_ttcap->force_c_scale_b_box_height * unit_y;
		force_c_lsb_x = max_advance_height
		    * ins_ttcap->force_c_scale_lsb * unit_y;
		force_c_rsb_x = max_advance_height
		    * ins_ttcap->force_c_scale_rsb * unit_y;
	    }
	    /* calculate correction ratio */
	    force_c_width = (int)floor(force_c_width_x * scale + 0.5);
	    new_width = force_c_width;
	    if( ins_ttcap->flags & TTCAP_DOUBLE_STRIKE_CORRECT_B_BOX_WIDTH )
		force_c_width += ins_ttcap->doubleStrikeShift;
	    new_width += ins_ttcap->force_c_adjust_width_by_pixel;
	    force_c_ratio = (double)new_width/force_c_width;
	    force_c_width = new_width;
	    /* force_c_lsb, force_c_rsb */
	    if( ins_ttcap->flags & TTCAP_FORCE_C_LSB_FLAG )
		force_c_lsb = (int)floor( force_c_lsb_x * scale + 0.5 );
	    else
		force_c_lsb = minLsb;
	    if( ins_ttcap->flags & TTCAP_FORCE_C_RSB_FLAG )
		force_c_rsb = (int)floor( force_c_rsb_x * scale + 0.5 );
	    else
		force_c_rsb = maxRsb;
	    /* calculate shift of BitmapAutoItalic
	       (when diagonal matrix only) */
	    if( orig_is_matrix_unit == True ) {
		if( ins_ttcap->autoItalic != 0 ) {
		    double ai;
		    int ai_lsb,ai_rsb,ai_total;
		    if( 0 < ins_ttcap->autoItalic ) ai=ins_ttcap->autoItalic;
		    else ai = -ins_ttcap->autoItalic;
		    ai_total = (int)( (ascent+descent) * ai + 0.5);
		    ai_rsb = (int)((double)ai_total * ascent / ( ascent + descent ) + 0.5 );
		    ai_lsb = -(ai_total - ai_rsb);
		    if( 0 < ins_ttcap->autoItalic ) {
			ins_ttcap->lsbShiftOfBitmapAutoItalic = ai_lsb;
			ins_ttcap->rsbShiftOfBitmapAutoItalic = ai_rsb;
		    }
		    else {
			ins_ttcap->lsbShiftOfBitmapAutoItalic = -ai_rsb;
			ins_ttcap->rsbShiftOfBitmapAutoItalic = -ai_lsb;
		    }
		}
	    }
	    /* integer adjustment by TTCap */
	    if( ins_ttcap->flags & TTCAP_DOUBLE_STRIKE )
		maxRsb += ins_ttcap->doubleStrikeShift;
	    maxRsb += ins_ttcap->adjustRightSideBearingByPixel;
	    minLsb += ins_ttcap->adjustLeftSideBearingByPixel;
	    /* */
	    if( ins_ttcap->flags & TTCAP_DOUBLE_STRIKE )
		force_c_rsb += ins_ttcap->doubleStrikeShift;
	    force_c_rsb += ins_ttcap->force_c_adjust_rsb_by_pixel;
	    force_c_lsb += ins_ttcap->force_c_adjust_lsb_by_pixel;
	    /* apply to rawWidth */
	    averageWidth = (int)floor(10 * width_x * scale 
				      * ratio + 0.5);
	    rawWidth = floor(width_x * scale
			     * ratio * 1000. / base_height + 0.5);
	    rawAverageWidth = floor(width_x * scale * ratio * 10.
				    * 1000. / base_height + 0.5);
	    force_c_rawWidth = floor(force_c_width_x * scale
				     * force_c_ratio * 1000. / base_height + 0.5);
	    /* */
	}
	/* for BITMAP fonts [if(face->bitmap)] */
	else {
	    /* These values differ from actual when outline,
	       so we must use them ONLY FOR BITMAP. */
	    width = (int)floor(smetrics->max_advance * ins_ttcap->scaleBBoxWidth / 64.0 + .5);
	    descent = -smetrics->descender / 64;
	    ascent = smetrics->ascender / 64;
	    /* force constant */
	    force_c_width = (int)floor(smetrics->max_advance
				       * ins_ttcap->force_c_scale_b_box_width / 64.0 + .5);
	    /* Preserve average width for bitmap fonts */
	    if(vals->width != 0)
		averageWidth = (int)floor(vals->width * ins_ttcap->scaleBBoxWidth +.5);
	    else
		averageWidth = (int)floor(10.0 * smetrics->max_advance
					  * ins_ttcap->scaleBBoxWidth / 64.0 + .5);
	    rawWidth = 0;
	    rawAverageWidth = 0;
	    force_c_rawWidth = 0;
	    /* We don't consider vertical layouts */
	    advance = (int)floor(smetrics->max_advance / 64.0 +.5);
	    unit_x = vals->pixel_matrix[0];
	    unit_y = 0;
	    /* We can use 'width' only when bitmap.
	       This should not be set when outline. */
	    minLsb = 0;
	    maxRsb = width;
	    /* force constant */
	    if( ins_ttcap->flags & TTCAP_FORCE_C_LSB_FLAG )
		force_c_lsb = (int)floor(smetrics->max_advance
					 * ins_ttcap->force_c_scale_lsb / 64.0 + .5);
	    else
		force_c_lsb = minLsb;
	    if( ins_ttcap->flags & TTCAP_FORCE_C_RSB_FLAG )
		force_c_rsb = (int)floor(smetrics->max_advance
					 * ins_ttcap->force_c_scale_rsb / 64.0 + .5);
	    else
		force_c_rsb = maxRsb;
	    /* calculate shift of BitmapAutoItalic */
	    if( ins_ttcap->autoItalic != 0 ) {
		double ai;
		int ai_lsb,ai_rsb,ai_total;
		if( 0 < ins_ttcap->autoItalic ) ai=ins_ttcap->autoItalic;
		else ai = -ins_ttcap->autoItalic;
		ai_total = (int)( (ascent+descent) * ai + 0.5);
		ai_rsb = (int)((double)ai_total * ascent / ( ascent + descent ) + 0.5 );
		ai_lsb = -(ai_total - ai_rsb);
		if( 0 < ins_ttcap->autoItalic ) {
		    ins_ttcap->lsbShiftOfBitmapAutoItalic = ai_lsb;
		    ins_ttcap->rsbShiftOfBitmapAutoItalic = ai_rsb;
		}
		else {
		    ins_ttcap->lsbShiftOfBitmapAutoItalic = -ai_rsb;
		    ins_ttcap->rsbShiftOfBitmapAutoItalic = -ai_lsb;
		}
	    }
	    /* integer adjustment by TTCap */
	    if( ins_ttcap->flags & TTCAP_DOUBLE_STRIKE_CORRECT_B_BOX_WIDTH )
		width += ins_ttcap->doubleStrikeShift;
	    if( ins_ttcap->flags & TTCAP_DOUBLE_STRIKE )
		maxRsb += ins_ttcap->doubleStrikeShift;
	    maxRsb += ins_ttcap->adjustRightSideBearingByPixel;
	    minLsb += ins_ttcap->adjustLeftSideBearingByPixel;
	    /* We have not carried out matrix calculation, so this is done. */
	    maxRsb += ins_ttcap->rsbShiftOfBitmapAutoItalic;
	    minLsb += ins_ttcap->lsbShiftOfBitmapAutoItalic;
	    /* force constant */
	    if( ins_ttcap->flags & TTCAP_DOUBLE_STRIKE )
		force_c_rsb += ins_ttcap->doubleStrikeShift;
	    force_c_rsb += ins_ttcap->force_c_adjust_rsb_by_pixel;
	    force_c_lsb += ins_ttcap->force_c_adjust_lsb_by_pixel;
	    force_c_rsb += ins_ttcap->rsbShiftOfBitmapAutoItalic;
	    force_c_lsb += ins_ttcap->lsbShiftOfBitmapAutoItalic;
	}

	/* SET CALCULATED VALUES TO INSTANCE */

	/* Set actual height and cosine */
	instance->pixel_size = base_height;
	instance->advance = advance;
	if ( unit_x != 0 ){
	    instance->pixel_width_unit_x = unit_x/base_height;
	    instance->pixel_width_unit_y = 0;
	}
	else{
	    instance->pixel_width_unit_x = 0;
	    instance->pixel_width_unit_y = unit_y/base_height;
	}

	/* header's metrics */
	instance->charcellMetrics = (xCharInfo*)xalloc(sizeof(xCharInfo));
	if(instance->charcellMetrics == NULL) {
	    xrc = AllocError;
	    goto quit;
	}
	instance->charcellMetrics->ascent = ascent;
	instance->charcellMetrics->descent = descent;
	instance->charcellMetrics->attributes = rawWidth;
	instance->charcellMetrics->rightSideBearing = maxRsb;
	instance->charcellMetrics->leftSideBearing = minLsb;
	instance->charcellMetrics->characterWidth = width;
	instance->averageWidth = averageWidth;
	instance->rawAverageWidth = rawAverageWidth;

	/* Check code 0 */
	if( FreeTypeInstanceGetGlyph(font->zero_idx, 0, &tmpglyph, font->instance) != Successful
	    || tmpglyph == NULL)
	    if( FreeTypeInstanceGetGlyph(font->zero_idx, FT_GET_DUMMY, &tmpglyph, font->instance)
		!= Successful )
		tmpglyph = NULL;
	if ( !tmpglyph ) {
	    xrc = AllocError;
	    goto quit;
	}

	/* FORCE CONSTANT METRICS */
	if( 0 <= ins_ttcap->forceConstantSpacingEnd ) {
            xCharInfo *tmpchar = NULL;
            int c = ins_ttcap->force_c_representative_metrics_char_code;
	    /* header's metrics */
	    if( instance->forceConstantMetrics == NULL ){
		instance->forceConstantMetrics = (xCharInfo*)xalloc(sizeof(xCharInfo));
		if(instance->forceConstantMetrics == NULL) {
		    xrc = AllocError;
		    goto quit;
		}
	    }
            /* Get Representative Metrics */
            if ( 0 <= c ) {
		if( FreeTypeFontGetGlyphMetrics(c, 0, &tmpchar, font) != Successful )
		    tmpchar = NULL;
            }
            if ( tmpchar && 0 < tmpchar->characterWidth ) {
		instance->forceConstantMetrics->leftSideBearing  = tmpchar->leftSideBearing;
		instance->forceConstantMetrics->rightSideBearing = tmpchar->rightSideBearing;
		instance->forceConstantMetrics->characterWidth   = tmpchar->characterWidth;
		instance->forceConstantMetrics->ascent           = tmpchar->ascent;
		instance->forceConstantMetrics->descent          = tmpchar->descent;
		instance->forceConstantMetrics->attributes       = tmpchar->attributes;
            }
            else {
                instance->forceConstantMetrics->leftSideBearing  = force_c_lsb;
                instance->forceConstantMetrics->rightSideBearing = force_c_rsb;
                instance->forceConstantMetrics->characterWidth   = force_c_width;
                instance->forceConstantMetrics->ascent           = ascent;
                instance->forceConstantMetrics->descent          = descent;
                instance->forceConstantMetrics->attributes       = force_c_rawWidth;
            }
	    /* Check code 0 */
	    if( FreeTypeInstanceGetGlyph(font->zero_idx, FT_FORCE_CONSTANT_SPACING, 
					 &tmpglyph, font->instance) != Successful
		|| tmpglyph == NULL)
		if( FreeTypeInstanceGetGlyph(font->zero_idx, FT_FORCE_CONSTANT_SPACING | FT_GET_DUMMY,
					     &tmpglyph, font->instance)
		    != Successful )
		    tmpglyph = NULL;
	    if ( !tmpglyph ) {
		xrc = AllocError;
		goto quit;
	    }
        }
    }
    else{

	/*
	 * CACHED VALUES
	 */

	width = instance->charcellMetrics->characterWidth;
	ascent = instance->charcellMetrics->ascent;
	descent = instance->charcellMetrics->descent;
	rawWidth = instance->charcellMetrics->attributes;
	maxRsb = instance->charcellMetrics->rightSideBearing;
	minLsb = instance->charcellMetrics->leftSideBearing;
	averageWidth = instance->averageWidth;
	rawAverageWidth = instance->rawAverageWidth;

    }

    /*
     * SET maxbounds, minbounds ...
     */

    if( !charcell ) {		/* NOT CHARCELL */
	if( info ){
	    /* 
	       Calculate all glyphs' metrics.
	       maxbounds.ascent and maxbounds.descent are quite important values
	       for XAA.  If ascent/descent of each glyph exceeds 
	       maxbounds.ascent/maxbounds.descent, XAA causes SERVER CRASH.
	       Therefore, THIS MUST BE DONE.
	    */
	    ft_compute_bounds(font,info,vals);
	}
    }
    else{			/* CHARCELL */

    /*
     * SET CALCULATED OR CACHED VARIABLES
     */

	vals->width = averageWidth;
	
	if( info ){

	    info->maxbounds.leftSideBearing = minLsb;
	    info->maxbounds.rightSideBearing = maxRsb;
	    info->maxbounds.characterWidth = width;
	    info->maxbounds.ascent = ascent;
	    info->maxbounds.descent = descent;
	    info->maxbounds.attributes =
		(unsigned short)(short)rawWidth;
	    
	    info->minbounds = info->maxbounds;
	}
    }

    /* set info */

    if( info ){
	/*
	info->fontAscent = ascent;
	info->fontDescent = descent;
	*/
	info->fontAscent = info->maxbounds.ascent;
	info->fontDescent = info->maxbounds.descent;
	/* Glyph metrics are accurate */
	info->inkMetrics=1;
	    
	memcpy((char *)&info->ink_maxbounds,
	       (char *)&info->maxbounds, sizeof(xCharInfo));
	memcpy((char *)&info->ink_minbounds,
	       (char *)&info->minbounds, sizeof(xCharInfo));
	
	/* XXX - hack */
	info->defaultCh=0;

	/* Set the pInfo flags */
	/* Properties set by FontComputeInfoAccelerators:
	   pInfo->noOverlap;
	   pInfo->terminalFont;
	   pInfo->constantMetrics;
	   pInfo->constantWidth;
	   pInfo->inkInside;
	*/
	/* from lib/font/util/fontaccel.c */
	FontComputeInfoAccelerators(info);
    }

    if(xf)
        xf->fontPrivate = (void*)font;
  
    if(info) {
        xrc = FreeTypeAddProperties(font, vals, info, entry->name.name, 
                                    rawAverageWidth, font_properties);
        if (xrc != Successful) {
            goto quit;
        }
    }

 quit:
    if ( dynStrTTCapCodeRange ) xfree(dynStrTTCapCodeRange);
    if ( dynStrFTFileName ) xfree(dynStrFTFileName);
    if ( dynStrRealFileName ) xfree(dynStrRealFileName);
    if ( xrc != Successful ) {
	if( font ){
	    if( face && font->instance == NULL ) FreeTypeFreeFace(face);
	    FreeTypeFreeFont(font);
	}
    }
    return xrc;
}

/* Routines used by X11 to get info and glyphs from the font. */

static int
FreeTypeGetMetrics(FontPtr pFont, unsigned long count, unsigned char *chars,
                   FontEncoding charEncoding, unsigned long *metricCount,
                   xCharInfo **metrics)
{
    unsigned int code = 0;
    int flags = 0;
    FTFontPtr tf;
    struct TTCapInfo *ttcap;
    xCharInfo **mp, *m;

    /*  MUMBLE1("Get metrics for %ld characters\n", count);*/

    tf = (FTFontPtr)pFont->fontPrivate;
    ttcap = &tf->instance->ttcap;
    mp = metrics;

    while (count-- > 0) {
        switch (charEncoding) {
        case Linear8Bit: 
        case TwoD8Bit:
            code = *chars++;
            break;
        case Linear16Bit: 
        case TwoD16Bit:
            code = (*chars++ << 8);
            code |= *chars++;
	    /* */
            if ( !(ttcap->flags & TTCAP_FORCE_C_OUTSIDE) ) {
                if ( (int)code <= ttcap->forceConstantSpacingEnd 
		     && ttcap->forceConstantSpacingBegin <= (int)code )
                    flags|=FT_FORCE_CONSTANT_SPACING;
		else flags=0;
            }
            else {      /* for GB18030 proportional */
                if ( (int)code <= ttcap->forceConstantSpacingEnd 
		     || ttcap->forceConstantSpacingBegin <= (int)code )
                    flags|=FT_FORCE_CONSTANT_SPACING;
		else flags=0;
            }
            break;
        }

        if(FreeTypeFontGetGlyphMetrics(code, flags, &m, tf) == Successful && m!=NULL) {
            *mp++ = m;
        }
#ifdef X_ACCEPTS_NO_SUCH_CHAR
	else *mp++ = &noSuchChar.metrics;
#endif
    }
    
    *metricCount = mp - metrics;
    return Successful;
}

static int
FreeTypeGetGlyphs(FontPtr pFont, unsigned long count, unsigned char *chars,
                  FontEncoding charEncoding, unsigned long *glyphCount,
                  CharInfoPtr *glyphs)
{
    unsigned int code = 0;
    int flags = 0;
    FTFontPtr tf;
    CharInfoPtr *gp;
    CharInfoPtr g;
    struct TTCapInfo *ttcap;

    tf = (FTFontPtr)pFont->fontPrivate;
    ttcap = &tf->instance->ttcap;
    gp = glyphs;

    while (count-- > 0) {
        switch (charEncoding) {
        case Linear8Bit: case TwoD8Bit:
            code = *chars++;
            break;
        case Linear16Bit: case TwoD16Bit:
            code = *chars++ << 8; 
            code |= *chars++;
	    /* */
            if ( !(ttcap->flags & TTCAP_FORCE_C_OUTSIDE) ) {
                if ( (int)code <= ttcap->forceConstantSpacingEnd 
		     && ttcap->forceConstantSpacingBegin <= (int)code )
                    flags|=FT_FORCE_CONSTANT_SPACING;
		else flags=0;
            }
            else {      /* for GB18030 proportional */
                if ( (int)code <= ttcap->forceConstantSpacingEnd 
		     || ttcap->forceConstantSpacingBegin <= (int)code )
                    flags|=FT_FORCE_CONSTANT_SPACING;
		else flags=0;
            }
            break;
        }
      
        if(FreeTypeFontGetGlyph(code, flags, &g, tf) == Successful && g!=NULL) {
            *gp++ = g;
        }
#ifdef X_ACCEPTS_NO_SUCH_CHAR
	else {
#ifdef XAA_ACCEPTS_NULL_BITS
	    *gp++ = &noSuchChar;
#else
	    if ( tf->dummy_char.bits ) {
		*gp++ = &tf->dummy_char;
	    }
	    else {
		char *raster = NULL;
		int wd_actual, ht_actual, wd, ht, bpr;
		wd_actual = tf->info->maxbounds.rightSideBearing - tf->info->maxbounds.leftSideBearing;
		ht_actual = tf->info->maxbounds.ascent + tf->info->maxbounds.descent;
		if(wd_actual <= 0) wd = 1;
		else wd=wd_actual;
		if(ht_actual <= 0) ht = 1;
		else ht=ht_actual;
		bpr = (((wd + (tf->instance->bmfmt.glyph<<3) - 1) >> 3) & 
		       -tf->instance->bmfmt.glyph);
		raster = (char*)xalloc(ht * bpr);
		if(raster) {
		    memset(raster, 0, ht * bpr);
		    tf->dummy_char.bits = raster;
		    *gp++ = &tf->dummy_char;
		}
	    }
#endif
	}
#endif
    }
    
    *glyphCount = gp - glyphs;
    return Successful;
}

static int
FreeTypeSetUpFont(FontPathElementPtr fpe, FontPtr xf, FontInfoPtr info, 
                  fsBitmapFormat format, fsBitmapFormatMask fmask,
                  FontBitmapFormatPtr bmfmt)
{
    int xrc;
    int image;

    /* Get the default bitmap format information for this X installation.
       Also update it for the client if running in the font server. */
    FontDefaultFormat(&bmfmt->bit, &bmfmt->byte, &bmfmt->glyph, &bmfmt->scan);
    if ((xrc = CheckFSFormat(format, fmask, &bmfmt->bit, &bmfmt->byte,
                             &bmfmt->scan, &bmfmt->glyph,
                             &image)) != Successful) {
        MUMBLE1("Aborting after checking FS format: %d\n", xrc);
        return xrc;
    }

    if(xf) {
        xf->refcnt = 0;
        xf->bit = bmfmt->bit;
        xf->byte = bmfmt->byte;
        xf->glyph = bmfmt->glyph;
        xf->scan = bmfmt->scan;
        xf->format = format;
        xf->get_glyphs = FreeTypeGetGlyphs;
        xf->get_metrics = FreeTypeGetMetrics;
        xf->unload_font = FreeTypeUnloadXFont;
        xf->unload_glyphs = 0;
        xf->fpe = fpe;
        xf->svrPrivate = 0;
        xf->fontPrivate = 0;        /* we'll set it later */
        xf->fpePrivate = 0;
    }

    info->defaultCh = 0;
    info->noOverlap = 0;          /* not updated */
    info->terminalFont = 0;       /* not updated */
    info->constantMetrics = 0;    /* we'll set it later */
    info->constantWidth = 0;      /* we'll set it later */
    info->inkInside = 1;
    info->inkMetrics = 1;
    info->allExist=0;             /* not updated */
    info->drawDirection = LeftToRight; /* we'll set it later */
    info->cachable = 1;           /* we don't do licensing */
    info->anamorphic = 0;         /* can hinting lead to anamorphic scaling? */
    info->maxOverlap = 0;         /* we'll set it later. */
    info->pad = 0;                /* ??? */
    return Successful;
}

/* Functions exported by the backend */

static int
FreeTypeOpenScalable(FontPathElementPtr fpe, FontPtr *ppFont, int flags,
                     FontEntryPtr entry, char *fileName, FontScalablePtr vals,
                     fsBitmapFormat format, fsBitmapFormatMask fmask,
                     FontPtr non_cachable_font)
{
    int xrc;
    FontPtr xf;
    FontBitmapFormatRec bmfmt;

    MUMBLE1("Open Scalable %s, XLFD=",fileName);
#ifdef DEBUG_TRUETYPE
    fwrite(entry->name.name, entry->name.length, 1, stdout);
#endif
    MUMBLE("\n");

    xf = CreateFontRec();
    if (xf == NULL)
        return AllocError;

    xrc = FreeTypeSetUpFont(fpe, xf, &xf->info, format, fmask, &bmfmt);
    if(xrc != Successful) {
        DestroyFontRec(xf);
        return xrc;
    }
    xrc = FreeTypeLoadXFont(fileName, vals, xf, &xf->info, &bmfmt, entry);
    if(xrc != Successful) {
        MUMBLE1("Error during load: %d\n",xrc);
        DestroyFontRec(xf);
        return xrc;
    }

    *ppFont = xf;

    return xrc;
}

/* Routine to get requested font info. */

static int
FreeTypeGetInfoScalable(FontPathElementPtr fpe, FontInfoPtr info,
                        FontEntryPtr entry, FontNamePtr fontName,
                        char *fileName, FontScalablePtr vals)
{
    int xrc;
    FontBitmapFormatRec bmfmt;

    MUMBLE("Get info, XLFD= ");
#ifdef DEBUG_TRUETYPE
    fwrite(entry->name.name, entry->name.length, 1, stdout);
#endif
    MUMBLE("\n");

    xrc = FreeTypeSetUpFont(fpe, 0, info, 0, 0, &bmfmt);
    if(xrc != Successful) {
        return xrc;
    }

    bmfmt.glyph <<= 3;

    xrc = FreeTypeLoadXFont(fileName, vals, 0, info, &bmfmt, entry);
    if(xrc != Successful) {
        MUMBLE1("Error during load: %d\n", xrc);
        return xrc;
    }

    return Successful;
}

/* Renderer registration. */

/* Set the capabilities of this renderer. */
#define CAPABILITIES (CAP_CHARSUBSETTING | CAP_MATRIX)

/* Set it up so file names with either upper or lower case can be
   loaded.  We don't support compressed fonts. */
static FontRendererRec renderers[] = {
    {".ttf", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".TTF", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".ttc", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".TTC", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".otf", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".OTF", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".otc", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".OTC", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".pfa", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".PFA", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".pfb", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".PFB", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
};
static int num_renderers = sizeof(renderers) / sizeof(renderers[0]);

static FontRendererRec alt_renderers[] = {
    {".bdf", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".BDF", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".pcf", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
    {".PCF", 4, 0, FreeTypeOpenScalable, 0,
     FreeTypeGetInfoScalable, 0, CAPABILITIES},
};

static int num_alt_renderers =  
sizeof(alt_renderers) / sizeof(alt_renderers[0]);
    

void
FreeTypeRegisterFontFileFunctions(void)
{
    int i;

    for (i = 0; i < num_renderers; i++)
        FontFileRegisterRenderer(&renderers[i]);

    for (i = 0; i < num_alt_renderers; i++)
        FontFilePriorityRegisterRenderer(&alt_renderers[i], -10);
}
