/*
   GSXftFontInfo

   NSFont helper for GNUstep GUI X/GPS Backend

   Copyright (C) 1996 Free Software Foundation, Inc.

   Author:  Fred Kiefer <fredkiefer@gmx.de>
   Date: July 2001

   This file is part of the GNUstep GUI X/GPS Backend.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; see the file COPYING.LIB.
   If not, see <http://www.gnu.org/licenses/> or write to the 
   Free Software Foundation, 51 Franklin Street, Fifth Floor, 
   Boston, MA 02110-1301, USA.
*/

#include "config.h"
#include "xlib/XGContext.h"
#include "xlib/XGPrivate.h"
#include "xlib/XGGState.h"
#include "x11/XGServer.h"
#include <Foundation/NSByteOrder.h>
#include <Foundation/NSCharacterSet.h>
#include <Foundation/NSData.h>
#include <Foundation/NSDebug.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSValue.h>
// For the encoding functions
#include <GNUstepBase/Unicode.h>

#include <AppKit/NSBezierPath.h>
#include "xlib/GSXftFontInfo.h"

#ifdef HAVE_FC
#define id _gs_avoid_id_collision
#include <fontconfig/fontconfig.h>
#undef id

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_OUTLINE_H

/*
 * class global dictionary of existing fonts
 */
static NSMutableDictionary *allFonts = nil;

// just a warpper around a FcPattern, to make it a NSObject
@interface FcFont : NSObject
{
  FcPattern *aFont;
}
- initWithPattern:(FcPattern *)aFace;
- (FcPattern *)font;
@end

@implementation FcFont

- initWithPattern:(FcPattern *)aFace
{
  [super init];
  aFont = aFace;
  FcPatternReference(aFace);
  return self;
}

- (FcPattern *)font
{
  return aFont;
}

- (void) dealloc
{
  FcPatternDestroy(aFont);  
  [super dealloc];
}
@end

@implementation FcFontEnumerator

// Make a GNUStep style font descriptor from a FcPattern
static NSArray *faFromFc(FcPattern *pat)
{
  int weight, slant, spacing, nsweight;
  unsigned int nstraits = 0;
  char *family;
  NSMutableString *name, *style;

  if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &weight) != FcResultMatch ||
      FcPatternGetInteger(pat, FC_SLANT,  0, &slant) != FcResultMatch ||
      FcPatternGetString(pat, FC_FAMILY, 0, (FcChar8 **)&family) != FcResultMatch)
    return nil;

  if (FcPatternGetInteger(pat, FC_SPACING, 0, &spacing) == FcResultMatch)
    if (spacing==FC_MONO || spacing==FC_CHARCELL)
      nstraits |= NSFixedPitchFontMask;
  
  name = [NSMutableString stringWithCapacity: 100];
  style = [NSMutableString stringWithCapacity: 100];
  [name appendString: [NSString stringWithUTF8String: family]];

  switch (weight) 
    {
    case FC_WEIGHT_LIGHT:
      [style appendString: @"Light"];
      nsweight = 3;
      break;
    case FC_WEIGHT_MEDIUM:
      nsweight = 6;
      break;
    case FC_WEIGHT_DEMIBOLD:
      [style appendString: @"Demibold"];
      nsweight = 7;
      break;
    case FC_WEIGHT_BOLD:
      [style appendString: @"Bold"];
      nsweight = 9;
      nstraits |= NSBoldFontMask;
      break;
    case FC_WEIGHT_BLACK:
      [style appendString: @"Black"];
      nsweight = 12;
      nstraits |= NSBoldFontMask;
      break;
    default:
      nsweight = 6;
    }

  switch (slant) 
    {
    case FC_SLANT_ROMAN:
      break;
    case FC_SLANT_ITALIC:
      [style appendString: @"Italic"];
      nstraits |= NSItalicFontMask;
      break;
    case FC_SLANT_OBLIQUE:
      [style appendString: @"Oblique"];
      nstraits |= NSItalicFontMask;
      break;
    }

  if ([style length] > 0)
    {
      [name appendString: @"-"];
      [name appendString: style];
    }
  else
    {
      [style appendString: @"Roman"];
    }

  return [NSArray arrayWithObjects: name, 
		  style, 
		  [NSNumber numberWithInt: nsweight], 
		  [NSNumber numberWithUnsignedInt: nstraits],
		  nil];
}

- (void) enumerateFontsAndFamilies
{
  int i;
  NSMutableDictionary *fcxft_allFontFamilies = [[NSMutableDictionary alloc] init];
  NSMutableDictionary *fcxft_allFonts = [[NSMutableDictionary alloc] init];
  NSMutableArray *fcxft_allFontNames = [[NSMutableArray alloc] init];

  FcPattern *pat = FcPatternCreate();
  FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, FC_SLANT, FC_WEIGHT, 
                                     FC_SPACING, NULL);
  FcFontSet *fs = FcFontList(0, pat, os);

  FcPatternDestroy(pat);
  FcObjectSetDestroy(os);

  for (i=0; i < fs->nfont; i++)
    {
      char *family;

      if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, (FcChar8 **)&family) == FcResultMatch)
        {
          NSArray *fontArray;

          if ((fontArray = faFromFc(fs->fonts[i])))
            {
	      NSString *familyString;
	      NSMutableArray *familyArray;
              FcFont *aFont;
	      NSString *name = [fontArray objectAtIndex: 0];

              familyString = [NSString stringWithUTF8String: family];
              if (!(familyArray = [fcxft_allFontFamilies objectForKey: familyString]))
                {
                  familyArray = [[NSMutableArray alloc] init];
                  [fcxft_allFontFamilies setObject: familyArray forKey: familyString];
		  RELEASE(familyArray);
                }
              NSDebugLLog(@"NSFont", @"fc enumerator: adding font: %@", name);
              [familyArray addObject: fontArray];
              [fcxft_allFontNames addObject: name];      
              aFont = [[FcFont alloc] initWithPattern: fs->fonts[i]];
              [fcxft_allFonts setObject: aFont forKey: name];
              RELEASE(aFont);
            }
        }
    }
  FcFontSetDestroy (fs); 

  allFontNames = fcxft_allFontNames;
  allFontFamilies = fcxft_allFontFamilies;
  allFonts = fcxft_allFonts;
}

-(NSString *) defaultSystemFontName
{
  if ([allFontNames containsObject: @"Bitstream Vera Sans"])
    return @"Bitstream Vera Sans";
  if ([allFontNames containsObject: @"FreeSans"])
    return @"FreeSans";
  if ([allFontNames containsObject: @"DejaVu Sans"])
    return @"DejaVu Sans";
  return @"Helvetica";
}

-(NSString *) defaultBoldSystemFontName
{
  if ([allFontNames containsObject: @"Bitstream Vera Sans-Bold"])
    return @"Bitstream Vera Sans-Bold";
  if ([allFontNames containsObject: @"FreeSans-Bold"])
    return @"FreeSans-Bold";
  if ([allFontNames containsObject: @"DejaVu Sans-Bold"])
    return @"DejaVu Sans-Bold";
  return @"Helvetica-Bold";
}

-(NSString *) defaultFixedPitchFontName
{
  if ([allFontNames containsObject: @"Bitstream Vera Sans Mono"])
    return @"Bitstream Vera Sans Mono";
  if ([allFontNames containsObject: @"FreeMono"])
    return @"FreeMono";
  if ([allFontNames containsObject: @"DejaVu Sans Mono"])
    return @"DejaVu Sans Mono";
  return @"Courier";
}

@end
#endif

@interface GSXftFontInfo (Private)

- (BOOL) setupAttributes;
- (XGlyphInfo *)xGlyphInfo: (NSGlyph) glyph;

@end

@implementation GSXftFontInfo

- initWithFontName: (NSString*)name
	    matrix: (const CGFloat*)fmatrix
	screenFont: (BOOL)screenFont
{
  if (screenFont)
    {
      RELEASE(self);
      return nil;
    }

  [super init];
  ASSIGN(fontName, name);
  memcpy(matrix, fmatrix, sizeof(matrix));

  if (![self setupAttributes])
    {
      RELEASE(self);
      return nil;
    }

  return self;
}

- (void) dealloc
{
  if (font_info != NULL)
    XftFontClose([XGServer currentXDisplay], (XftFont *)font_info);
  [super dealloc];
}

#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
#if __INT_MIN__ == 0x7fffffff
#define Ones(mask) __builtin_popcount(mask)
#else
#define Ones(mask) __builtin_popcountl((mask) & 0xffffffff)
#endif
#else
/* Otherwise fall back on HACKMEM 169.  */
static int
Ones(unsigned int n)
{
  register unsigned int tmp;
    
  tmp = n - ((n >> 1) & 033333333333)
      - ((n >> 2) & 011111111111);
  return ((tmp + (tmp >> 3)) & 030707070707) % 63;
}
#endif

- (NSCharacterSet*) coveredCharacterSet
{
  if (coveredCharacterSet == nil)
    {
      if (!((XftFont *)font_info)->charset)
        return nil;
      else
        {
          NSMutableData	*d = [NSMutableData new];
          unsigned count = 0;
          FcCharSet *charset = ((XftFont *)font_info)->charset;
          FcChar32 ucs4;
          FcChar32 map[FC_CHARSET_MAP_SIZE];
          FcChar32 next;
          BOOL swap = NSHostByteOrder() == NS_BigEndian;

          if (!d)
            return nil;

          for (ucs4 = FcCharSetFirstPage(charset, map, &next);
               ucs4 != FC_CHARSET_DONE;
               ucs4 = FcCharSetNextPage(charset, map, &next))
            {
              unsigned int i;
              NSRange aRange;
              unsigned int max;

              aRange = NSMakeRange(ucs4, FC_CHARSET_MAP_SIZE * sizeof(FcChar32));
              max = NSMaxRange(aRange);
              // Round up to a suitable plane size
              max = ((max + 8191) >> 13) << 13;
              [d setLength: max];

              for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
                if (map[i])
                  {
                    // Do some byte swapping, if needed.
                    if (swap)
                      {
                        map[i] = NSSwapInt(map[i]);
                      }
                    count += Ones(map[i]);
                  }
            
              [d replaceBytesInRange: aRange withBytes: map];
            }

          ASSIGN(coveredCharacterSet, 
                 [NSCharacterSet characterSetWithBitmapRepresentation: d]);
          numberOfGlyphs = count;
          RELEASE(d);
        }
    }

  return coveredCharacterSet;
}

- (CGFloat) widthOfString: (NSString*)string
{
  XGlyphInfo extents;
  int len = [string length];
  XftChar16 str[len]; 

  [string getCharacters: (unichar*)str];
  XftTextExtents16 ([XGServer currentXDisplay],
		    font_info,
		    str, 
		    len,
		    &extents);

  return extents.width;
}

- (CGFloat) widthOfGlyphs: (const NSGlyph *) glyphs length: (int) len
{
  XGlyphInfo extents;
  XftChar16 buf[len];
  int i;

  for (i = 0; i < len; i++)
    {
      buf[i] = glyphs[i];
    }

  XftTextExtents16 ([XGServer currentXDisplay],
		    font_info,
		    buf,
		    len,
		    &extents);

  return extents.width;
}

- (NSMultibyteGlyphPacking)glyphPacking
{
  return NSTwoByteGlyphPacking;
}

- (NSSize) advancementForGlyph: (NSGlyph)glyph
{
  XGlyphInfo *pc = [self xGlyphInfo: glyph];

  // if per_char is NULL assume max bounds
  if (!pc)
    return  NSMakeSize((float)(font_info)->max_advance_width, 0);

  return NSMakeSize((float)pc->xOff, (float)pc->yOff);
}

- (NSRect) boundingRectForGlyph: (NSGlyph)glyph
{
  XGlyphInfo *pc = [self xGlyphInfo: glyph];

  // if per_char is NULL assume max bounds
  if (!pc)
      return NSMakeRect(0.0, 0.0,
		    (float)font_info->max_advance_width,
		    (float)(font_info->ascent + font_info->descent));

  return NSMakeRect((float)pc->x, (float)-pc->y, 
		    (float)(pc->width), 
		    (float)(pc->height));
}

- (BOOL) glyphIsEncoded: (NSGlyph)glyph
{
  return XftGlyphExists([XGServer currentXDisplay],
			(XftFont *)font_info, glyph);
}

- (NSGlyph) glyphWithName: (NSString*)glyphName
{
  // FIXME: There is a mismatch between PS names and X names, that we should 
  // try to correct here
  KeySym k = XStringToKeysym([glyphName cString]);

  if (k == NoSymbol)
    return 0;
  else
    return (NSGlyph)k;
}

- (NSPoint) positionOfGlyph: (NSGlyph)curGlyph
	    precededByGlyph: (NSGlyph)prevGlyph
		  isNominal: (BOOL*)nominal
{
  if (nominal)
    *nominal = YES;

  if (curGlyph == NSControlGlyph || prevGlyph == NSControlGlyph)
    return NSZeroPoint;

//  if (curGlyph == NSNullGlyph)
    {
      NSSize advance = [self advancementForGlyph: prevGlyph];
      return NSMakePoint(advance.width, advance.height);
    }
}

/*
- (CGFloat) pointSize
{
  Display	*xdpy = [XGServer currentXDisplay];

  return XGFontPointSize(xdpy, font_info);
}
*/

- (void) drawString:  (NSString*)string
	  onDisplay: (Display*) xdpy drawable: (Drawable) draw
	       with: (GC) xgcntxt at: (XPoint) xp
{
  NSData *d = [string dataUsingEncoding: mostCompatibleStringEncoding
		      allowLossyConversion: YES];
  int length = [d length];
  const char *cstr = (const char*)[d bytes];
  XGGState *state = (XGGState *)[(XGContext *)GSCurrentContext() currentGState];
  XftDraw *xftdraw = [state xftDrawForDrawable: draw];
  XftColor xftcolor = [state xftColor];

  /* do it */
  XftDrawString16(xftdraw, &xftcolor, font_info, 
		  xp.x, xp.y, (XftChar16*)cstr, length);
}

- (void) drawGlyphs: (const NSGlyph *) glyphs length: (int) len
	  onDisplay: (Display*) xdpy drawable: (Drawable) draw
	       with: (GC) xgcntxt at: (XPoint) xp
{
  XGGState *state = (XGGState *)[(XGContext *)GSCurrentContext() currentGState];
  XftDraw *xftdraw = [state xftDrawForDrawable: draw];
  XftColor xftcolor = [state xftColor];
  XftChar16 buf[len];
  int i;
 
  for (i = 0; i < len; i++)
    {
      buf[i] = glyphs[i];
    }

  /* do it */
  XftDrawString16(xftdraw, &xftcolor, font_info, 
		  xp.x, xp.y, (XftChar16*)buf, len);
}

- (void) draw: (const char*) s length: (int) len 
    onDisplay: (Display*) xdpy drawable: (Drawable) draw
	 with: (GC) xgcntxt at: (XPoint) xp
{
  int length = strlen(s);
  XGGState *state = (XGGState *)[(XGContext *)GSCurrentContext() currentGState];
  XftDraw *xftdraw = [state xftDrawForDrawable: draw];
  XftColor xftcolor = [state xftColor];

#ifdef HAVE_UTF8
  /* do it */
  if (NSUTF8StringEncoding == mostCompatibleStringEncoding)
    {
      XftDrawStringUtf8(xftdraw, &xftcolor, font_info,
                        xp.x, xp.y, (XftChar8 *)s, length);
    }
  else
#endif
    {
      XftDrawString8(xftdraw, &xftcolor, font_info, 
                   xp.x, xp.y, (XftChar8*)s, length);
    }
}

- (CGFloat) widthOf: (const char*) s length: (int) len
{
  XGlyphInfo extents;

#ifdef HAVE_UTF8
  if (mostCompatibleStringEncoding == NSUTF8StringEncoding)
    XftTextExtentsUtf8([XGServer currentXDisplay],
                       font_info,
                       (XftChar8 *)s,
                       len,
                       &extents);
  else
#endif
    XftTextExtents8([XGServer currentXDisplay],
                    font_info,
                    (XftChar8*)s, 
                    len,
                    &extents);

  return extents.width;
}


- (void) setActiveFor: (Display*) xdpy gc: (GC) xgcntxt
{
}

static int bezierpath_move_to(const FT_Vector *to, void *user)
{
  NSBezierPath *path = (NSBezierPath *)user;
  NSPoint d;

  d.x = to->x / 65536.0;
  d.y = to->y / 65536.0;
/*
  d.x = to->x;
  d.y = to->y;
*/
  [path closePath];
  [path moveToPoint: d];
  return 0;
}

static int bezierpath_line_to(const FT_Vector *to, void *user)
{
  NSBezierPath *path = (NSBezierPath *)user;
  NSPoint d;

  d.x = to->x / 65536.0;
  d.y = to->y / 65536.0;
/*
  d.x = to->x;
  d.y = to->y;
*/
  [path lineToPoint: d];
  return 0;
}

static int bezierpath_conic_to(const FT_Vector *c1, const FT_Vector *to, void *user)
{
  NSBezierPath *path = (NSBezierPath *)user;
  NSPoint a, b, c, d;

  a = [path currentPoint];
  d.x = to->x / 65536.0;
  d.y = to->y / 65536.0;
  b.x = c1->x / 65536.0;
  b.y = c1->y / 65536.0;
/*
  d.x = to->x;
  d.y = to->y;
  b.x = c1->x;
  b.y = c1->y;
*/
  c.x = (b.x * 2 + d.x) / 3.0;
  c.y = (b.y * 2 + d.y) / 3.0;
  b.x = (b.x * 2 + a.x) / 3.0;
  b.y = (b.y * 2 + a.y) / 3.0;
  [path curveToPoint: d controlPoint1: b controlPoint2: c];
  return 0;
}

static int bezierpath_cubic_to(const FT_Vector *c1, const FT_Vector *c2, 
                               const FT_Vector *to, void *user)
{
  NSBezierPath *path = (NSBezierPath *)user;
  NSPoint b, c, d;

  b.x = c1->x / 65536.0;
  b.y = c1->y / 65536.0;
  c.x = c2->x / 65536.0;
  c.y = c2->y / 65536.0;
  d.x = to->x / 65536.0;
  d.y = to->y / 65536.0;
/*
  b.x = c1->x;
  b.y = c1->y;
  c.x = c2->x;
  c.y = c2->y;
  d.x = to->x;
  d.y = to->y;
*/
  [path curveToPoint: d controlPoint1: b controlPoint2: c];
  return 0;
}

static FT_Outline_Funcs bezierpath_funcs = {
  move_to: bezierpath_move_to,
  line_to: bezierpath_line_to,
  conic_to: bezierpath_conic_to,
  cubic_to: bezierpath_cubic_to,
  shift: 10,
//  delta: 0,
};

- (void) appendBezierPathWithGlyphs: (NSGlyph *)glyphs
                              count: (int)count
                       toBezierPath: (NSBezierPath *)path
{
  int i;
  FT_Matrix ftmatrix;
  FT_Vector ftdelta;
  FT_Face face;
  FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP;
  NSPoint p = [path currentPoint];

  ftmatrix.xx = 65536;
  ftmatrix.xy = 0;
  ftmatrix.yx = 0;
  ftmatrix.yy = 65536;
  ftdelta.x = p.x * 64.0;
  ftdelta.y = p.y * 64.0;

  face = XftLockFace((XftFont *)font_info);
  for (i = 0; i < count; i++)
    {
      NSGlyph glyph;
      FT_Glyph gl;
      FT_OutlineGlyph og;

      glyph = glyphs[i];
      // FIXME: Should do this conversion in the glyph creation!
      glyph = XftCharIndex([XGServer currentXDisplay], 
                           (XftFont *)font_info, glyph);
     
      if (FT_Load_Glyph(face, glyph, load_flags))
        continue;

      if (FT_Get_Glyph(face->glyph, &gl))
        continue;

      if (FT_Glyph_Transform(gl, &ftmatrix, &ftdelta))
        {
          NSLog(@"glyph transformation failed!");
          continue;
        }

      og = (FT_OutlineGlyph)gl;

      ftdelta.x += gl->advance.x >> 10;
      ftdelta.y += gl->advance.y >> 10;

      FT_Outline_Decompose(&og->outline, &bezierpath_funcs, path);

      FT_Done_Glyph(gl);
    }

  XftUnlockFace((XftFont *)font_info);

  if (count)
    {
      [path moveToPoint: NSMakePoint(ftdelta.x / 64.0, ftdelta.y / 64.0)];
    }
}

@end

@implementation GSXftFontInfo (Private)

- (BOOL) setupAttributes
{
  Display *xdpy = [XGServer currentXDisplay];
  int defaultScreen = DefaultScreen(xdpy);

#ifdef HAVE_FC
  FcFont *realFont = [allFonts objectForKey: fontName];
  FcPattern *fontPattern;
  FcPattern *pattern; 
  FcResult fc_result;
  char *family;
  int fcspacing, fcweight, fcslant;

  if (!realFont)
    {
      return NO;
    }

  if (!xdpy)
    return NO;

  fontPattern = FcPatternDuplicate([realFont font]);

  // the only thing needs customization here is the size
  // FIXME: It would be correcter to use FC_SIZE as GNUstep should be
  // using point measurements, but as the rest of the library uses pixel,
  // we need to stick with that here.
  FcPatternAddDouble(fontPattern, FC_PIXEL_SIZE, (double)(matrix[0]));
  // Should do this only when size > 8
  FcPatternAddBool(fontPattern, FC_AUTOHINT, FcTrue);
  pattern = XftFontMatch(xdpy, defaultScreen, fontPattern, &fc_result);
  // tide up
  FcPatternDestroy(fontPattern);
  
  if (FcPatternGetString(pattern, FC_FAMILY, 0, (FcChar8 **)&family) == FcResultMatch)
    {
      ASSIGN(familyName, [NSString stringWithUTF8String: (const char*)family]);
    }
  if (FcPatternGetInteger(pattern, FC_SPACING, 0, &fcspacing) == FcResultMatch)
    {
      isFixedPitch = (fcspacing == FC_MONO || fcspacing == FC_CHARCELL);
    }
  if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &fcweight) == FcResultMatch)
    {
      switch (fcweight)
        {
        case FC_WEIGHT_LIGHT:
            weight = 3;
            break;
        case FC_WEIGHT_MEDIUM:
            weight = 6;
            break;
        case FC_WEIGHT_DEMIBOLD:
            weight = 7;
            break;
        case FC_WEIGHT_BOLD:
            weight = 9;
            break;
        case FC_WEIGHT_BLACK:
            weight = 12;
            break;
	default:
          // Don't know
          weight = 6;
	}
    }

  if (FcPatternGetInteger(pattern, FC_SLANT,  0, &fcslant) == FcResultMatch)
    {
      switch (fcslant) 
        {
        case FC_SLANT_ROMAN:
          traits |= NSUnitalicFontMask;
          break;
        case FC_SLANT_ITALIC:
          traits |= NSItalicFontMask;
          break;
        case FC_SLANT_OBLIQUE:
          traits |= NSItalicFontMask;
          break;
       }
    }

  // Derek Zhou claims that this takes over the ownership of the pattern
  if ((font_info = XftFontOpenPattern(xdpy, pattern)))
    {
      NSDebugLLog(@"NSFont", @"Loaded font: %@", fontName);
    }
  else
    {
      NSDebugLLog(@"NSFont", @"Cannot load font: %@", fontName);
      return NO;
    }

  /* TODO: somehow make gnustep-gui send unicode our way. utf8? ugly, but it works */
  mostCompatibleStringEncoding = NSUTF8StringEncoding;
  encodingScheme = @"iso10646-1";
#else 
  NSString *reg;
  XftPattern *pattern;
  XftResult result;
  NSString *xfontname;
  char *xftTypeString;
  int xftTypeInt;
  NSArray *encoding;

  if (!xdpy)
    return NO;

  // Retrieve the XLFD matching the given fontName. DPS->X.
  xfontname = XGXFontName(fontName, matrix[0]);

  // Load Xft font and get font info structure.
  if ((xfontname == nil) ||
      (font_info = XftFontOpenXlfd(xdpy, defaultScreen, [xfontname UTF8String])) == NULL)
    {
      NSLog(@"Unable to open fixed font %@", xfontname);
      return NO;
    }
  else
    NSDebugLog(@"Loaded font: %@", xfontname);

  pattern = font_info->pattern;
  result = XftPatternGetString(pattern, XFT_FAMILY, 0, &xftTypeString);
  if (result != XftResultTypeMismatch)
    {
      ASSIGN(familyName,
         [NSString stringWithUTF8String: (const char*)xftTypeString]);
    }
  result = XftPatternGetInteger(pattern, XFT_SPACING, 0, &xftTypeInt);
  if (result != XftResultTypeMismatch)
    {
      isFixedPitch = (xftTypeInt != 0);
    }

  result = XftPatternGetInteger(pattern, XFT_WEIGHT, 0, &xftTypeInt);
  if (result != XftResultTypeMismatch)
    {
      switch (xftTypeInt)
        {
        case 0:
          weight = 3;
          break;
        case 100:
          weight = 6;
          break;
        case 180:
          weight = 7;
          break;
        case 200:
          weight = 9;
          break;
        case 210:
          weight = 12;
          break;
        default:
          // Don't know
          weight = 6;;
        }
    }

  result = XftPatternGetInteger(pattern, XFT_SLANT, 0, &xftTypeInt);
  if (result != XftResultTypeMismatch)
    {
      if (xftTypeInt != 0)
        traits |= NSItalicFontMask;
      else
        traits |= NSUnitalicFontMask;
    }

  XftPatternGetString(pattern, XFT_ENCODING, 0, &xftTypeString);
  encodingScheme = [NSString stringWithUTF8String: xftTypeString];
  encoding = [encodingScheme componentsSeparatedByString: @"-"];
  reg = [encoding objectAtIndex: 0];
  if (reg != nil)
    { 
      if ([encoding count] > 1)
        {
	  NSString *enc = [encoding lastObject];
	  mostCompatibleStringEncoding = GSEncodingForRegistry(reg, enc);
	  if (mostCompatibleStringEncoding == GSUndefinedEncoding)
	    mostCompatibleStringEncoding = NSASCIIStringEncoding;
          if (mostCompatibleStringEncoding == NSUnicodeStringEncoding)
            mostCompatibleStringEncoding = NSUTF8StringEncoding;
	  RETAIN(encodingScheme);
	}
    }
  else
    encodingScheme = nil;
  // FIXME: italicAngle, underlinePosition, underlineThickness are not set.
  // Should use XA_ITALIC_ANGLE, XA_UNDERLINE_POSITION, XA_UNDERLINE_THICKNESS
#endif

  // Fill the ivars
  if (weight >= 9)
    traits |= NSBoldFontMask;
  else
    traits |= NSUnboldFontMask;

  if (isFixedPitch)
    traits |= NSFixedPitchFontMask;

  isBaseFont = NO;
  ascender = font_info->ascent;
  descender = -(font_info->descent);
  capHeight = ascender - descender;   // TODO
  xHeight = capHeight*0.6;   //Errr... TODO
  fontBBox = NSMakeRect(
    (float)(0),
    (float)(-font_info->descent),
    (float)(font_info->max_advance_width),
    (float)(font_info->ascent + font_info->descent));
  maximumAdvancement = NSMakeSize(font_info->max_advance_width, 0.0);
  minimumAdvancement = NSMakeSize(0,0);
//   printf("h=%g  a=%g d=%g  max=(%g %g)  (%g %g)+(%g %g)\n",
//          xHeight, ascender, descender,
//          maximumAdvancement.width, maximumAdvancement.height,
//          fontBBox.origin.x, fontBBox.origin.y,
//          fontBBox.size.width, fontBBox.size.height);

  return YES;
}

- (XGlyphInfo *)xGlyphInfo: (NSGlyph) glyph
{
  static XGlyphInfo glyphInfo;

  XftTextExtents32 ([XGServer currentXDisplay],
                    (XftFont *)font_info,
                    &glyph,
                    1,
                    &glyphInfo);

  return &glyphInfo;
}

@end
