#   include	"appFrameConfig.h"

#   include	<stddef.h>
#   include	<stdio.h>
#   include	<stdlib.h>
#   include	<string.h>

#   include	"drawImpl.h"
#   include	"appScreenFont.h"
#   include	<psGlyphs.h>
#   include	<psFontName.h>
#   include	<uniLegacyEncoding.h>
#   include	"appMatchFont.h"
#   include	<uniAlternatives.h>

#   include	<appDebugon.h>

#   ifdef USE_FONTCONFIG			/*  {{	*/

#   include	<fontconfig/fontconfig.h>
#   include	<ft2build.h>
#   include	FT_FREETYPE_H

static int		APP_FontConfigInitialized= 0;
static FcConfig *	APP_FcConfig= 0;

/* Cope with older freetype versions: */
# if ! defined(FT_ENCODING_UNICODE) && defined(ft_encoding_unicode)
# define FT_ENCODING_UNICODE ft_encoding_unicode
# endif
# if ! defined(FT_ENCODING_MS_SYMBOL) && defined(ft_encoding_symbol)
# define FT_ENCODING_MS_SYMBOL ft_encoding_symbol
# endif

static const char * appFtErrorStr( int e );

static int		APP_FreeTypeInitialized= 0;
static FT_Library	APP_FT_Library;

static void appFcScaleRect(	DocumentRectangle *	abb,
				int			upm )
    {
    abb->drX0= ( 1000* abb->drX0 )/ upm;
    abb->drX1= ( 1000* abb->drX1 )/ upm;
    abb->drY0= ( 1000* abb->drY0 )/ upm;
    abb->drY1= ( 1000* abb->drY1 )/ upm;
    }

static void appFcScaleInt(	int *		pV,
				int		upm )
    {
    *pV= ( 1000* *pV )/ upm;
    }

/************************************************************************/
/*									*/
/*  Set a mapping from character encoding to glyph index for this face.	*/
/*  Base the mapping on the unicode encoding of the face. The encoding	*/
/*  has already been set as the current encoding.			*/
/*									*/
/************************************************************************/

static int appFcGetEncodingsFromUnicode(AfmFontInfo *		afi,
					const FT_Face		ftFace )
    {
    FT_ULong	charcode;
    FT_UInt	glyphIdx;

    charcode= FT_Get_First_Char( ftFace, &glyphIdx );
    while( glyphIdx != 0 )
	{
	if  ( glyphIdx < afi->afiMetricCount )
	    {
	    if  ( charcode == 'x' )
		{ afi->afiXHeight= afi->afiMetrics[glyphIdx]->acmBBox.drY1; }
	    if  ( charcode == 'H' )
		{ afi->afiCapHeight= afi->afiMetrics[glyphIdx]->acmBBox.drY1; }

	    /*  Only support UTF16 */
	    if  ( charcode <= 0xffff )
		{
		if  ( psFontInfoSetGlyphUnicode( afi, glyphIdx, charcode ) )
		    { SLXDEB(afi->afiFontName,glyphIdx,charcode); return -1; }

		if  ( ! FT_HAS_GLYPH_NAMES( ftFace )	&&
		      ! afi->afiMetrics[glyphIdx]->acmN	)
		    {
		    psFontInfoSetGlyphName( afi, glyphIdx,
					psUnicodeToGlyphName( charcode ) );
		    }
		}
	    }

	charcode= FT_Get_Next_Char( ftFace, charcode, &glyphIdx );
	}

    return 0;
    }

/************************************************************************/
/*									*/
/*  Set a mapping from character encoding to glyph index for this face.	*/
/*  Base the mapping on the known mapping from dingbats to unicode.	*/
/*									*/
/************************************************************************/

static int appFcGetEncodingsFromAdobeDingbats(
					AfmFontInfo *		afi,
					const FT_Face		ftFace )
    {
    FT_ULong	charcode;
    FT_UInt	glyphIdx;

    charcode= FT_Get_First_Char( ftFace, &glyphIdx );
    while ( glyphIdx != 0 )
	{
	if  ( charcode < 256 )
	    {
	    int unicode= uniDingbatsGlyphUnicodes[charcode];

	    if  ( unicode >= 0						&&
		  psFontInfoSetGlyphUnicode( afi, glyphIdx, unicode )	)
		{ LLLDEB(glyphIdx,charcode,unicode); return -1;	}
	    }
	else{ LDEB(charcode);	}

	charcode= FT_Get_Next_Char( ftFace, charcode, &glyphIdx );
	}

    return 0;
    }

/************************************************************************/
/*									*/
/*  Set a mapping from character encoding to glyph index for this face.	*/
/*  Base the mapping on a preset encoding for the face.			*/
/*									*/
/************************************************************************/

static int appFcGetDefaultEncoding(	AfmFontInfo *		afi,
					const FT_Face		ftFace )
    {
    int		ch;

    for ( ch= 0; ch < 256; ch++ )
	{
	int		code= ch;
	int		glyphIdx;

	glyphIdx= FT_Get_Char_Index( ftFace, code );
	if  ( glyphIdx <= 0 )
	    {
	    code= uniGetAlternative( code );
	    if  ( code >= 0 )
		{ glyphIdx= FT_Get_Char_Index( ftFace, code );	}
	    }

	/* HACK MS Symbol */
	if  ( glyphIdx <= 0 )
	    { glyphIdx= FT_Get_Char_Index( ftFace, 61440+ ch );	}

	if  ( glyphIdx <= 0 || glyphIdx >= afi->afiMetricCount )
	    {
	    /* SLDEB(afi->afiFullName,glyphIdx); */
	    continue;
	    }

	if  ( code == 'x' )
	    { afi->afiXHeight= afi->afiMetrics[glyphIdx]->acmBBox.drY1; }
	if  ( code == 'H' )
	    { afi->afiCapHeight= afi->afiMetrics[glyphIdx]->acmBBox.drY1; }

	if  ( psFontInfoSetGlyphCode( afi, glyphIdx, ch ) )
	    { LLDEB(glyphIdx,ch); return -1;	}
	}

    return 0;
    }

/************************************************************************/
/*									*/
/*  Diagnostic routine: Show the encodings of a font.			*/
/*									*/
/************************************************************************/

static void appFcShowCharmaps(	const AfmFontInfo *	afi,
				FT_Face			ftFace )
    {
    int		cm;

    SLDEB(afi->afiFullName,ftFace->num_charmaps);
    for ( cm= 0; cm < ftFace->num_charmaps; cm++ )
	{
	FT_CharMap *	ftCharMap= ftFace->charmaps+ cm;
	char 		s[5];
	int		fterror;

	FT_ULong	charcode;
	FT_UInt		glyphIdx;

	s[0]= ( (*ftCharMap)->encoding >> 24 ) & 0xff;
	s[1]= ( (*ftCharMap)->encoding >> 16 ) & 0xff;
	s[2]= ( (*ftCharMap)->encoding >>  8 ) & 0xff;
	s[3]= ( (*ftCharMap)->encoding       ) & 0xff;
	s[4]= '\0';
	LSLLDEB(cm,s,(*ftCharMap)->platform_id,(*ftCharMap)->encoding_id);

	fterror= FT_Set_Charmap( ftFace, *ftCharMap );
	if  ( fterror )
	    { LSLDEB(cm,s,fterror); continue;	}

	charcode= FT_Get_First_Char( ftFace, &glyphIdx );
	while ( glyphIdx != 0 )
	    {
	    charcode= FT_Get_Next_Char( ftFace, charcode, &glyphIdx );

	    if  ( charcode < 256 )
		{
		unsigned int	ch= charcode&0xff;
		if  ( glyphIdx < afi->afiMetricCount )
		    { CLSDEB(ch,glyphIdx,afi->afiMetrics[glyphIdx]->acmN); }
		else{ CLLDEB(ch,glyphIdx,afi->afiMetricCount); }
		}
	    else{
		if  ( glyphIdx < afi->afiMetricCount )
		    { XLSDEB(charcode,glyphIdx,afi->afiMetrics[glyphIdx]->acmN); }
		else{ XLLDEB(charcode,glyphIdx,afi->afiMetricCount); }
		}
	    }
	}

    return;
    }

/************************************************************************/
/*									*/
/*  Get the metrics of a font. As, with freetype, this can be an	*/
/*  expensive operation. This is deferred until the font is actually	*/
/*  used.								*/
/*									*/
/*  1)  Open the face.							*/
/*  2)  Get the name that can be used in PostScript output.		*/
/*  3)  Get metrics for the face as a whole. XHeight and CapHeight are	*/
/*	approximated at first and will be updated when the 'x' or the	*/
/*	'H' are encountered on the road.				*/
/*  4)  Get metrics and the name of the individual glyphs.		*/
/*									*/
/************************************************************************/

int appFcGetFontMetrics(	AfmFontInfo *	afi )
    {
    int			rval= 0;
    FT_Face		ftFace= (FT_Face)0;
    int			fterror;
    int			glyphIdx;
    const char *	fontName;
    int			invalidComposites= 0;

    /*  1  */
    fterror= FT_New_Face( APP_FT_Library,
			utilMemoryBufferGetString( &(afi->afiFontFileName) ),
			afi->afiFontFileIndex,
			&ftFace );
    if  ( fterror )
	{
	LLDEB(afi->afiFontFileIndex,fterror);
	return -1;
	}

    /*  2  */
    fontName= FT_Get_Postscript_Name( ftFace );
    if  ( fontName )
	{
	char *	s;
	char *	fn= strdup( fontName );
	if  ( ! fn )
	    { XDEB(fn); rval= -1; goto ready;	}

	s= fn;
	while( *s )
	    {
	    if  ( *s == ' ' )
		{ *s=   '-';	}
	    s++;
	    }

	if  ( afi->afiFontName )
	    { free( afi->afiFontName );	}

	afi->afiFontName= fn;
	}

    /*  3  */
    afi->afiFontBBox.drX0= ftFace->bbox.xMin;
    afi->afiFontBBox.drX1= ftFace->bbox.xMax;
    afi->afiFontBBox.drY0= ftFace->bbox.yMin;
    afi->afiFontBBox.drY1= ftFace->bbox.yMax;

    afi->afiAscender= ftFace->ascender;
    afi->afiDescender= ftFace->descender;
    afi->afiUnderlinePosition= ftFace->underline_position;
    afi->afiUnderlineThickness= ftFace->underline_thickness;

    afi->afiCapHeight= afi->afiAscender;
    afi->afiXHeight= ftFace->units_per_EM/ 2;

    appFcScaleRect( &(afi->afiFontBBox), ftFace->units_per_EM );
    appFcScaleInt( &(afi->afiAscender), ftFace->units_per_EM );
    appFcScaleInt( &(afi->afiDescender), ftFace->units_per_EM );
    appFcScaleInt( &(afi->afiUnderlinePosition), ftFace->units_per_EM );
    appFcScaleInt( &(afi->afiUnderlineThickness), ftFace->units_per_EM );

    appFcScaleInt( &(afi->afiCapHeight), ftFace->units_per_EM );
    appFcScaleInt( &(afi->afiXHeight), ftFace->units_per_EM );

    /*  4  */
    for ( glyphIdx= 0; glyphIdx < ftFace->num_glyphs; glyphIdx++ )
	{
	DocumentRectangle	abb;
	int			C= -1;
	int			WX;
	char *			N= (char *)0;
	char			n[200];

	fterror= FT_Load_Glyph( ftFace, glyphIdx,
			    FT_LOAD_NO_SCALE|FT_LOAD_IGNORE_TRANSFORM );
	if  ( fterror )
	    {
	    if  ( fterror == FT_Err_Invalid_Composite )
		{
		if  ( invalidComposites )
		    { continue;	}

		invalidComposites= 1;
		}

	    LSDEB(glyphIdx,appFtErrorStr(fterror));
	    continue;
	    }

	WX= ftFace->glyph->metrics.horiAdvance;
	abb.drX0= ftFace->glyph->metrics.horiBearingX;
	abb.drX1= abb.drX0+ ftFace->glyph->metrics.width;
	abb.drY1= ftFace->glyph->metrics.horiBearingY;
	abb.drY0= abb.drY1- ftFace->glyph->metrics.height;

	appFcScaleInt( &WX, ftFace->units_per_EM );
	appFcScaleRect( &abb, ftFace->units_per_EM );

	if  ( FT_HAS_GLYPH_NAMES( ftFace ) )
	    {
	    if  ( FT_Get_Glyph_Name( ftFace, glyphIdx, n, sizeof(n) ) )
		{ LDEB(glyphIdx);	}
	    else{ N= n;			}
	    }

	if  ( psFontInfoAddMetric( afi, C, WX, N, &abb ) )
	    { LDEB(1); rval= -1; goto ready;	}

	/* TTF default char */
	if  ( afi->afiFallbackGlyph < 0		&&
	      N && ! strcmp( N, ".notdef" )	)
	    { afi->afiFallbackGlyph= glyphIdx;	}
	}

    /*  Derive Dingbats mapping via known mapping */
    if  ( ! strcmp(afi->afiFamilyName, "Dingbats" )		||
          ! strcmp(afi->afiFamilyName, "ITC Zapf Dingbats" )	)
	{
	fterror= FT_Select_Charmap( ftFace, FT_ENCODING_ADOBE_CUSTOM );
	if  ( ! fterror )
	    { appFcGetEncodingsFromAdobeDingbats( afi, ftFace );	}
	}

    /*  5  */
    fterror= FT_Select_Charmap( ftFace, FT_ENCODING_UNICODE );
    if  ( ! fterror )
	{ appFcGetEncodingsFromUnicode( afi, ftFace );	}

    if  ( afi->afiMetricsDeferred )
	{
	fterror= FT_Select_Charmap( ftFace, FT_ENCODING_MS_SYMBOL );
	if  ( ! fterror )
	    { appFcGetDefaultEncoding( afi, ftFace );	}
	}

    if  ( psGetUnicodesFromGlyphNames( afi ) )
	{ SDEB(afi->afiFamilyName);	}
    if  ( psGetAlternateGlyphs( afi ) )
	{ SDEB(afi->afiFamilyName);	}
    if  ( psResolveFallbackGlyph( afi ) )
	{ SDEB(afi->afiFamilyName);	}

    afi->afiMetricsDeferred= 0;

    if  ( afi->afiMetricsDeferred )
	{
	SDEB(afi->afiFontName);
	appFcShowCharmaps( afi, ftFace );
	afi->afiMetricsDeferred= 0; rval= -1; goto ready;
	}
  ready:

    if  ( ftFace )
	{ FT_Done_Face( ftFace );	}

    return rval;
    }

/************************************************************************/
/*									*/
/*  Handle one font in the list of fonts that we retrieved through	*/
/*  fontconfig.								*/
/*									*/
/*  If the font implements some of the font-encodings supported by Ted,	*/
/*  it is remembered in the PostScript font list.			*/
/*									*/
/************************************************************************/

static int appFcListOneFont(	PostScriptFontList *	psfl,
				FcPattern *		font )
    {
    int			rval= 0;

    FcChar8 *		family= NULL;
    FcChar8 *		style= NULL;
    int			width= -1;
    int			weight= -1;
    int			slant= -1;
    int			spacing= -1;
    FcChar8 *		file= (FcChar8 *)0;
    int			idx= 0;

    char *		fontName= (char *)0;
    char *		fullName= (char *)0;
    AfmFontInfo *	afi= (AfmFontInfo *)0;

    FcPatternGetString( font, FC_FAMILY, 0, &family );
    FcPatternGetString( font, FC_STYLE, 0, &style );
    FcPatternGetInteger( font, FC_WEIGHT, 0, &weight );
    FcPatternGetInteger( font, FC_WIDTH, 0, &width );
    FcPatternGetInteger( font, FC_SLANT, 0, &slant );
    FcPatternGetInteger( font, FC_SPACING, 0, &spacing );

    if  ( FcPatternGetString( font, FC_FILE, 0, &file ) != FcResultMatch )
	{ SDEB((char *)family); goto ready; }
    FcPatternGetInteger( font, FC_INDEX, 0, &idx );

    if  ( ! family )
	{ XDEB(family); goto ready;	}
    if  ( ! style )
	{ style= (FcChar8 *)"";		}

    /************************/
    {
    char *	s;

    fontName= (char *)malloc(
	    strlen( (char *)family )+ 1+ strlen( (char *)style )+ 1 );
    fullName= (char *)malloc(
	    strlen( (char *)family )+ 1+ strlen( (char *)style )+ 1 );

    if  ( strlen( (char *)style ) > 0 )
	{
	sprintf( fontName, "%s-%s", (char *)family, (char *)style );
	sprintf( fullName, "%s %s", (char *)family, (char *)style );
	}
    else{
	strcpy( fontName, (char *)family );
	strcpy( fullName, (char *)family );
	}

    s= fontName;
    while( *s )
	{
	if  ( *s == ' ' )
	    { *s=   '-';	}
	s++;
	}
    }
    /************************/

    /* A duplicate from a different file? */
    afi= psPostScriptFontListGetFontInfoByFontName( psfl, fontName );
    if  ( afi )
	{
	if  ( psPostScriptFontListSetFontInfoForFaceFile( psfl, afi,
							    (char *)file ) )
	    { SDEB((char *)file); rval= -1; goto ready;	}

	goto ready;
	}

    afi= (AfmFontInfo *)malloc( sizeof(AfmFontInfo) );
    if  ( ! afi )
	{ XDEB(afi); rval= -1; goto ready;	}
    psInitAfmFontInfo( afi );

    afi->afiFontName= fontName; fontName= (char *)0; /* steal */
    afi->afiFullName= fullName; fullName= (char *)0; /* steal */

    afi->afiFamilyName= strdup( (char *)family );
    afi->afiWeightStr= strdup( psWeightStr( weight ) );
    afi->afiWeightInt= weight;
    afi->afiWidthStr= strdup( psWidthStr( width ) );
    afi->afiWidthInt= width;

    if  ( slant > ( FC_SLANT_ROMAN+ 2* FC_SLANT_ITALIC )/ 3 )
	{ afi->afiItalicAngle= -13.0; /* typical negative value */	}
    else{ afi->afiItalicAngle= 0.0;					}

    afi->afiIsFixedPitch= spacing != FC_PROPORTIONAL;

    if  ( utilMemoryBufferSetString( &(afi->afiFontFileName), (char *)file ) )
	{ LDEB(1); rval= -1; goto ready;	}
    afi->afiFontFileIndex= idx;

    afi->afiMetricsDeferred= 1;
    afi->afiResolveMetrics= appFcGetFontMetrics;
    if  ( ! afi->afiMetricsDeferred )
	{
	if  ( appFcGetFontMetrics( afi ) )
	    { SLDEB((char *)file,idx); goto ready;	}
	}

    /* No: This is a font as it is found on the machine: use it without 
     * modifications.
    psMapVisibleNamesToBase35( afi );
    */

    if  ( psPostScriptFontListAddInfo( psfl, afi ) )
	{ LDEB(1); rval= -1; goto ready;	}

  ready:

    if  ( fontName )
	{ free( fontName );	}
    if  ( fullName )
	{ free( fullName );	}

    return rval;
    }

/************************************************************************/
/*									*/
/*  Include fonts that can be accessed via fontconfig/freetype/xft in	*/
/*  the PostScript font list.						*/
/*									*/
/************************************************************************/

int appFcListFonts(	PostScriptFontList *	psfl )
    {
    int			rval= 0;
    FcPattern *		pat= (FcPattern *)0;
    FcObjectSet *	os= (FcObjectSet *)0;
    FcFontSet *		fs= (FcFontSet *)0;

    int			i;

    if  ( ! APP_FontConfigInitialized )
	{
	if  ( FcInit() != FcTrue )
	    { LDEB(1); rval= -1; goto ready;	}

	APP_FontConfigInitialized= 1;
	}

    if  ( ! APP_FreeTypeInitialized )
	{
	int	fterror= FT_Init_FreeType( &APP_FT_Library );

	if  ( fterror )
	    { LDEB(fterror); rval= -1; goto ready;	}

	APP_FreeTypeInitialized= 1;
	}

    pat= FcPatternCreate();
    if  ( ! pat )
	{ XDEB(pat); rval= -1; goto ready;	}
    if  ( ! FcPatternAddBool( pat, FC_SCALABLE, FcTrue ) )
	{ SDEB(FC_SCALABLE); rval= -1; goto ready;	}

    os= FcObjectSetBuild(	FC_FAMILY,
				FC_WIDTH,
				FC_STYLE,
				FC_FILE,
				FC_INDEX,
				FC_CHARSET,
				FC_SLANT,
				FC_WEIGHT,
				FC_SPACING,
			(char *)0 );
    if  ( ! os )
	{ XDEB(os); rval= -1; goto ready;	}

    fs= FcFontList( APP_FcConfig, pat, os );
    if  ( ! fs )
	{ XDEB(fs); rval= -1; goto ready;	}

    for ( i= 0; i < fs->nfont; i++ )
	{
	if  ( appFcListOneFont( psfl, fs->fonts[i] ) )
	    { LDEB(i); rval= -1; goto ready;	}
	}

    if  ( psPostScriptFontListInfosToFamilies( psfl ) )
	{ LDEB(1); rval= -1; goto ready;	}

  ready:

    if  ( fs )
	{ FcFontSetDestroy( fs );	}
    if  ( os )
	{ FcObjectSetDestroy( os );	}
    if  ( pat )
	{ FcPatternDestroy( pat );	}

    return rval;
    }

/************************************************************************/

static int appFcAddChar(	int		unicode,
				void *		vcharset )
    {
    FcCharSetAddChar( (FcCharSet *)vcharset, unicode );

    return 0;
    }

/************************************************************************/

# define LOG_MATCHES 0

static AfmFontInfo * appFcGetFontInfoFromFont(
				FcPattern *			font,
				const PostScriptFontList *	psfl )
    {
    AfmFontInfo *	afi= (AfmFontInfo *)0;
    FcChar8 *		file= (FcChar8 *)0;
#   if LOG_MATCHES
    FcChar8 *		family= (FcChar8 *)0;
    FcChar8 *		style= (FcChar8 *)0;

    FcPatternGetString( font, FC_FAMILY, 0, &family );
    FcPatternGetString( font, FC_STYLE, 0, &style );
#   endif
    FcPatternGetString( font, FC_FILE, 0, &file );

#   if LOG_MATCHES
    appDebug( "TRY: file=\"%s\" family= \"%s\" style=\"%s\"\n",
				(char *)file, (char *)family, (char *)style );
#   endif

    afi= psPostScriptFontListGetFontInfoByFaceFile( psfl, (char *)file );
    if  ( ! afi )
	{ /*SXDEB((char *)file,afi);*/	}

    return afi;
    }

static AfmFontInfo * appFcGetFontInfoFromList(
				FcPattern *			pat,
				const PostScriptFontList *	psfl )
    {
    FcResult		fcResult;
    FcFontSet *		fs= (FcFontSet *)0;
    AfmFontInfo *	afi= (AfmFontInfo *)0;
    int			i;

    fs= FcFontSort( APP_FcConfig, pat, FcFalse, (FcCharSet **)0, &fcResult );
    if  ( ! fs )
	{ XLDEB(fs,fcResult); goto ready;	}

    for ( i= 0; i < fs->nfont; i++ )
	{
	FcPattern *	font= fs->fonts[i];

	afi= appFcGetFontInfoFromFont( font, psfl );
	if  ( afi )
	    {
#	    if LOG_MATCHES
	    appDebug( "GOT: \"%s\"\n", afi->afiFontName );
#	    endif
	    break;
	    }
	}

  ready:

    if  ( fs )
	{ FcFontSetDestroy( fs );	}

    return afi;
    }

/************************************************************************/
/*									*/
/*  THE font matching call for fonts via fontconfig:			*/
/*  Find the PostScript font family that matches a document font.	*/
/*									*/
/*  The implicit assumption is that the PostScript font list is		*/
/*  constructed with appFcListFonts().					*/
/*									*/
/************************************************************************/

AfmFontInfo * appFcGetFontInfoForAttribute(
				const char *			familyName,
				int				styleInt,
				const IndexSet *		unicodesUsed,
				const TextAttribute *		ta,
				const PostScriptFontList *	psfl )
    {
    AfmFontInfo *		rval= (AfmFontInfo *)0;

    FcPattern *			pat= (FcPattern *)0;
    FcPattern *			found= (FcPattern *)0;
    FcResult			fcResult;

    FcCharSet *			fcCharset= (FcCharSet *)0;

    int				slant= FC_SLANT_ROMAN;
    int				weight= FC_WEIGHT_REGULAR;

    int				width= FONTwidthNORMAL;
    char			scratch[200+1];

    if  ( unicodesUsed )
	{
	fcCharset= FcCharSetCreate();
	if  ( ! fcCharset )
	    { XDEB(fcCharset); goto ready;	}
	if  ( utilIndexSetForAll( unicodesUsed, appFcAddChar, fcCharset ) < 0 )
	    { LDEB(1); goto ready;	}
	}

    if  ( ta->taFontIsBold )
	{ weight= FC_WEIGHT_BOLD;	}
    if  ( ta->taFontIsSlanted )
	{ slant= FC_SLANT_ITALIC;	}

    if  ( psRemoveWidthFromName( scratch, sizeof(scratch)- 1, &width,
							    familyName ) < 0 )
	{ SDEB(familyName); goto ready;	}

    /*  HACK */
    if  ( ! strcmp( scratch, "ITC Zapf Dingbats" ) )
	{ strcpy( scratch, "Dingbats");	}

    pat= FcPatternCreate();
    if  ( ! pat )
	{ XDEB(pat); goto ready;	}
    if  ( ! FcPatternAddBool( pat, FC_SCALABLE, FcTrue ) )
	{ SDEB(FC_SCALABLE); goto ready;	}
    if  ( ! FcPatternAddString( pat, FC_FAMILY, (FcChar8 *)scratch ) )
	{ SDEB(FC_FAMILY); goto ready;	}
    if  ( ! FcPatternAddInteger( pat, FC_WIDTH, width ) )
	{ SDEB(FC_WIDTH); goto ready;	}
    if  ( ! FcPatternAddInteger( pat, FC_WEIGHT, weight ) )
	{ SDEB(FC_WEIGHT); goto ready;	}
    if  ( ! FcPatternAddInteger( pat, FC_SLANT, slant ) )
	{ SDEB(FC_SLANT); goto ready;	}
    if  ( fcCharset && ! FcPatternAddCharSet( pat, FC_CHARSET, fcCharset ) )
	{ SDEB(FC_CHARSET); goto ready;	}

    FcConfigSubstitute( APP_FcConfig, pat, FcMatchPattern );
    FcDefaultSubstitute( pat );

#   if LOG_MATCHES
    appDebug( "FIND: fam=\"%s\", width=%s\n",
				    scratch, psWidthStr( width ) );
#   endif

    found= FcFontMatch( APP_FcConfig, pat, &fcResult );
    if  ( ! found )
	{ SXLDEB(scratch,found,fcResult); goto ready;	}
    rval= appFcGetFontInfoFromFont( found, psfl );

    if  ( ! rval )
	{ rval= appFcGetFontInfoFromList( pat, psfl );	}

    if  ( ! rval )
	{ SXDEB(scratch,rval); goto ready;	}

  ready:

    if  ( pat )
	{ FcPatternDestroy( pat );	}
    if  ( found )
	{ FcPatternDestroy( found );	}
    if  ( fcCharset )
	{ FcCharSetDestroy( fcCharset );	}

    return rval;
    }

#   endif				/*  }}	*/

#   ifdef USE_XFT			/*  {{	*/

/************************************************************************/

static int appFontAddOneCharToSet(	int		val,
					void *		voidfcs )
    {
    FcCharSetAddChar((FcCharSet *)voidfcs, val );
    return 0;
    }

/************************************************************************/
/*									*/
/*  Open an XFT font corresponding to a (PostScript) font face in the	*/
/*  font catalog.							*/
/*									*/
/************************************************************************/

XftFont * drawOpenXftFont(	DrawScreenFont *	dsf )
    {
    const AfmFontInfo *	afi= dsf->apfPsFontInfo;
    XftFont *		rval= (XftFont *)0;

    int			screen;

    const char *	fc_charset= (const char *)0;
    FcCharSet *		fcCharset= (FcCharSet *)0;
    int			typeCharset= FcTypeVoid;

    int			width= FONTwidthNORMAL;
    char		scratch[200+1];

    if  ( psRemoveWidthFromName( scratch, sizeof(scratch)- 1, &width,
						    afi->afiFamilyName ) < 0 )
	{ SDEB(afi->afiFamilyName); goto ready;	}

    if  ( dsf->dsfUnicodesUsed )
	{
	fcCharset= FcCharSetCreate();
	fc_charset= FC_CHARSET;
	typeCharset= FcTypeCharSet;

	/* IndexSet -> FcCharSet */
	utilIndexSetForAll( dsf->dsfUnicodesUsed,
				appFontAddOneCharToSet, (void *)fcCharset );
 	}

    if  ( ! dsf->dsfDisplay )
	{ XDEB(dsf->dsfDisplay); return (XftFont *)0;	}
    screen= DefaultScreen( dsf->dsfDisplay );

    /*  2  */
    rval= XftFontOpen( dsf->dsfDisplay, screen,
	    FC_FAMILY, FcTypeString,
			scratch,
	    FC_WIDTH, FcTypeInteger,
			afi->afiWidthInt,
	    FC_PIXEL_SIZE, FcTypeDouble,
			(double)dsf->dsfSizePixels,
	    FC_SLANT, FcTypeInteger,
			(FONT_IS_SLANTED(afi)?FC_SLANT_ITALIC:FC_SLANT_ROMAN),
	    FC_WEIGHT, FcTypeInteger,
			(FONT_IS_BOLD(afi)?FC_WEIGHT_BOLD:FC_WEIGHT_REGULAR),
	    fc_charset, typeCharset,
			fcCharset,
	    NULL );

ready:

    if  ( fcCharset )
	{ FcCharSetDestroy( fcCharset );	}

    return rval;
    }

#   endif				/*  }}	*/

#   ifdef USE_FONTCONFIG
#	undef FT_ERRORDEF_
#	undef FT_NOERRORDEF_
#	define FT_ERRORDEF_( d, n, s ) { n, s, },
#	define FT_NOERRORDEF_( d, n, s ) { n, s, },

static const char * appFtErrorStr( int e )
    {
    int			i;
    static char		buf[20];

    typedef struct n2s { int n; const char * s; } n2s;

    static const n2s xftn2s[]=
    {
#   include <freetype2/fterrdef.h>
    };

    for ( i= 0; i < sizeof(xftn2s)/sizeof(n2s); i++ )
	{
	if  ( xftn2s[i].n == e )
	    { return xftn2s[i].s;	}
	}

    sprintf( buf, "0x%x", e );
    return buf;
    }

#   endif
