/*
 *	Font metric functions common to Type 1 (AFM) and TrueType font files.
 *  	Functions specific to Type 1 and TrueType fonts are in type1afm.c and
 *  	truetype.c respectively.
 *
 *	Copyright 1998  Huw D M Davies
 *  	Copyright 2001  Ian Pilcher
 *
 * 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.1 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; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"

#include <string.h>

#include "psdrv.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(psdrv);

/* ptr to fonts for which we have afm files */
FONTFAMILY *PSDRV_AFMFontList = NULL;


/***********************************************************
 *
 *	PSDRV_FreeAFMList
 *
 * Frees the family and afmlistentry structures in list head
 */
void PSDRV_FreeAFMList( FONTFAMILY *head )
{
    AFMLISTENTRY *afmle, *nexta;
    FONTFAMILY *family, *nextf;

    for(nextf = family = head; nextf; family = nextf) {
        for(nexta = afmle = family->afmlist; nexta; afmle = nexta) {
	    nexta = afmle->next;
	    HeapFree( PSDRV_Heap, 0, afmle );
	}
        nextf = family->next;
	HeapFree( PSDRV_Heap, 0, family );
    }
    return;
}


/***********************************************************
 *
 *	PSDRV_FindAFMinList
 * Returns ptr to an AFM if name (which is a PS font name) exists in list
 * headed by head.
 */
const AFM *PSDRV_FindAFMinList(FONTFAMILY *head, LPCSTR name)
{
    FONTFAMILY *family;
    AFMLISTENTRY *afmle;

    for(family = head; family; family = family->next) {
        for(afmle = family->afmlist; afmle; afmle = afmle->next) {
	    if(!strcmp(afmle->afm->FontName, name))
	        return afmle->afm;
	}
    }
    return NULL;
}

/***********************************************************
 *
 *	PSDRV_AddAFMtoList
 *
 *  Adds an afm to the list whose head is pointed to by head. Creates new
 *  family node if necessary and always creates a new AFMLISTENTRY.
 *
 *  Returns FALSE for memory allocation error; returns TRUE, but sets *p_added
 *  to FALSE, for duplicate.
 */
BOOL PSDRV_AddAFMtoList(FONTFAMILY **head, const AFM *afm, BOOL *p_added)
{
    FONTFAMILY *family = *head;
    FONTFAMILY **insert = head;
    AFMLISTENTRY *tmpafmle, *newafmle;

    newafmle = HeapAlloc(PSDRV_Heap, HEAP_ZERO_MEMORY,
			   sizeof(*newafmle));
    if (newafmle == NULL)
    	return FALSE;

    newafmle->afm = afm;

    while(family) {
        if(!strcmp(family->FamilyName, afm->FamilyName))
	    break;
	insert = &(family->next);
	family = family->next;
    }

    if(!family) {
        family = HeapAlloc(PSDRV_Heap, HEAP_ZERO_MEMORY,
			   sizeof(*family));
	if (family == NULL) {
	    HeapFree(PSDRV_Heap, 0, newafmle);
	    return FALSE;
	}
	*insert = family;
	if (!(family->FamilyName = HeapAlloc(PSDRV_Heap, 0, strlen(afm->FamilyName)+1 ))) {
	    HeapFree(PSDRV_Heap, 0, family);
	    HeapFree(PSDRV_Heap, 0, newafmle);
	    return FALSE;
	}
	strcpy( family->FamilyName, afm->FamilyName );
	family->afmlist = newafmle;
	*p_added = TRUE;
	return TRUE;
    }
    else {
    	tmpafmle = family->afmlist;
	while (tmpafmle) {
	    if (!strcmp(tmpafmle->afm->FontName, afm->FontName)) {
	    	WARN("Ignoring duplicate FontName '%s'\n", afm->FontName);
		HeapFree(PSDRV_Heap, 0, newafmle);
		*p_added = FALSE;
		return TRUE;	    	    	    /* not a fatal error */
	    }
	    tmpafmle = tmpafmle->next;
	}
    }

    tmpafmle = family->afmlist;
    while(tmpafmle->next)
        tmpafmle = tmpafmle->next;

    tmpafmle->next = newafmle;

    *p_added = TRUE;
    return TRUE;
}


/***********************************************************
 *
 *	PSDRV_DumpFontList
 *
 */
static void PSDRV_DumpFontList(void)
{
    FONTFAMILY      *family;
    AFMLISTENTRY    *afmle;

    for(family = PSDRV_AFMFontList; family; family = family->next) {
        TRACE("Family '%s'\n", family->FamilyName);
	for(afmle = family->afmlist; afmle; afmle = afmle->next)
	{
#if 0
	    INT i;
#endif

	    TRACE("\tFontName '%s' (%i glyphs) - '%s' encoding:\n",
	    	    afmle->afm->FontName, afmle->afm->NumofMetrics,
		    afmle->afm->EncodingScheme);

	    /* Uncomment to regenerate font data; see afm2c.c */

	    /* PSDRV_AFM2C(afmle->afm); */

#if 0
	    for (i = 0; i < afmle->afm->NumofMetrics; ++i)
	    {
	    	TRACE("\t\tU+%.4lX; C %i; N '%s'\n", afmle->afm->Metrics[i].UV,
		    	afmle->afm->Metrics[i].C, afmle->afm->Metrics[i].N->sz);
	    }
#endif
	}
    }
    return;
}


/*******************************************************************************
 *  PSDRV_CalcAvgCharWidth
 *
 *  Calculate WinMetrics.sAvgCharWidth for a Type 1 font.  Can also be used on
 *  TrueType fonts, if font designer set OS/2:xAvgCharWidth to zero.
 *
 *  Tries to use formula in TrueType specification; falls back to simple mean
 *  if any lowercase latin letter (or space) is not present.
 */
static inline SHORT MeanCharWidth(const AFM *afm)
{
    float   w = 0.0;
    int     i;

    for (i = 0; i < afm->NumofMetrics; ++i)
    	w += afm->Metrics[i].WX;

    w /= afm->NumofMetrics;

    return (SHORT)(w + 0.5);
}

static const struct { LONG UV; int weight; } UVweight[27] =
{
    { 0x0061,  64 }, { 0x0062,  14 }, { 0x0063,  27 }, { 0x0064,  35 },
    { 0x0065, 100 }, { 0x0066,  20 }, { 0x0067,  14 }, { 0x0068,  42 },
    { 0x0069,  63 }, { 0x006a,   3 }, { 0x006b,   6 }, { 0x006c,  35 },
    { 0x006d,  20 }, { 0x006e,  56 }, { 0x006f,  56 }, { 0x0070,  17 },
    { 0x0071,   4 }, { 0x0072,  49 }, { 0x0073,  56 }, { 0x0074,  71 },
    { 0x0075,  31 }, { 0x0076,  10 }, { 0x0077,  18 }, { 0x0078,   3 },
    { 0x0079,  18 }, { 0x007a,   2 }, { 0x0020, 166 }
};

SHORT PSDRV_CalcAvgCharWidth(const AFM *afm)
{
    float   w = 0.0;
    int     i;

    for (i = 0; i < 27; ++i)
    {
    	const AFMMETRICS    *afmm;

	afmm = PSDRV_UVMetrics(UVweight[i].UV, afm);
	if (afmm->UV != UVweight[i].UV)     /* UVMetrics returns first glyph */
	    return MeanCharWidth(afm);	    /*   in font if UV is missing    */

	w += afmm->WX * (float)(UVweight[i].weight);
    }

    w /= 1000.0;

    return (SHORT)(w + 0.5);
}


/*******************************************************************************
 *  AddBuiltinAFMs
 *
 */

static BOOL AddBuiltinAFMs(void)
{
    const AFM *const	*afm = PSDRV_BuiltinAFMs;

    while (*afm != NULL)
    {
    	BOOL	added;

    	if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, *afm, &added) == FALSE)
	    return FALSE;

	if (added == FALSE)
	    TRACE("Ignoring built-in font %s\n", (*afm)->FontName);

	++afm;
    }

    return TRUE;
}


/***********************************************************
 *
 *	PSDRV_GetFontMetrics
 *
 * Parses all afm files listed in [afmdirs] and [TrueType Font Directories]
 * sections of Wine configuration file.  Adds built-in data last, so it can
 * be overridden by user-supplied AFM or TTF files.
 *
 * If this function fails, PSDRV_Init will destroy PSDRV_Heap, so don't worry
 * about freeing all the memory that's been allocated.
 */

BOOL PSDRV_GetFontMetrics(void)
{
    if (PSDRV_GlyphListInit() != 0)
    	return FALSE;

    if (PSDRV_GetType1Metrics() == FALSE)
    	return FALSE;

#ifdef HAVE_FREETYPE
    if (PSDRV_GetTrueTypeMetrics() == FALSE)
    	return FALSE;
#endif

    if (AddBuiltinAFMs() == FALSE)
    	return FALSE;

    PSDRV_IndexGlyphList(); 	    /* Enable fast searching of glyph names */

    PSDRV_DumpFontList();

    return TRUE;
}
