Rewrite AFM parsing code

Ian Pilcher ian.pilcher at home.com
Wed Aug 8 15:13:04 CDT 2001


This restructures the AFM file parsing to more gracefully handle
constant built-in font data.

It also gets rid of the [afmfiles] config file option.

AFM file-specific code is now in type1afm.c; afm.c contains code common
to both AFM files and TrueType font files.

Modified files:
    dlls/wineps: Makefile.in afm.c psdrv.h truetype.c

Added files:
    dlls/wineps: type1afm.c

Log message:
    Ian Pilcher <ian.pilcher at home.com>
    WINEPS: rewrite and separate AFM parsing code (no more [afmfiles])
-- 
========================================================================
Ian Pilcher                                         ian.pilcher at home.com
========================================================================
-------------- next part --------------
diff -urN ../wine-20010726cvs/dlls/wineps/Makefile.in ./dlls/wineps/Makefile.in
--- ../wine-20010726cvs/dlls/wineps/Makefile.in	Wed Jun  6 15:22:05 2001
+++ ./dlls/wineps/Makefile.in	Wed Aug  8 00:22:08 2001
@@ -69,6 +69,7 @@
 	ps.c \
 	text.c \
 	truetype.c \
+	type1afm.c \
 	$(DATA_C_SRCS)
 
 RC_SRCS= \
diff -urN ../wine-20010726cvs/dlls/wineps/afm.c ./dlls/wineps/afm.c
--- ../wine-20010726cvs/dlls/wineps/afm.c	Thu Jul 26 20:33:09 2001
+++ ./dlls/wineps/afm.c	Wed Aug  8 13:39:16 2001
@@ -1,454 +1,25 @@
 /*
- *	Adobe Font Metric (AFM) file parsing
- *	See http://partners.adobe.com/asn/developer/PDFS/TN/5004.AFM_Spec.pdf
+ *	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
  * 
  */
 
 #include "config.h"
 
 #include <string.h>
-#include <stdlib.h> 	/* qsort() & bsearch() */
-#include <stdio.h>
-#include <dirent.h>
-#include <limits.h> 	/* INT_MIN */
-#ifdef HAVE_FLOAT_H
-# include <float.h>  	/* FLT_MAX */
-#endif
-#include "winnt.h"  	/* HEAP_ZERO_MEMORY */
-#include "winreg.h"
+
 #include "psdrv.h"
 #include "debugtools.h"
 
 DEFAULT_DEBUG_CHANNEL(psdrv);
-#include <ctype.h>
 
 /* ptr to fonts for which we have afm files */
 FONTFAMILY *PSDRV_AFMFontList = NULL;
 
-/* qsort/bsearch callback functions */
-typedef int (*compar_callback_fn) (const void *, const void *);
-
-static VOID SortFontMetrics(AFM *afm, AFMMETRICS *metrics);
-static VOID CalcWindowsMetrics(AFM *afm);
-static void PSDRV_ReencodeCharWidths(AFM *afm);
-
-/*******************************************************************************
- *  IsWinANSI
- *
- *  Checks whether Unicode value is part of Microsoft code page 1252
- *
- */
-static const INT ansiChars[21] =
-{
-    0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
-    0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
-    0x20ac, 0x2122, 0x2219
-};
-
-static int cmpUV(const INT *a, const INT *b)
-{
-    return *a - *b;
-}
- 
-inline static BOOL IsWinANSI(INT uv)
-{
-    if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) ||
-    	    (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) ||
-	    (0x2020 <= uv && uv <= 2022))
-    	return TRUE;
-	
-    if (bsearch(&uv, ansiChars, 21, sizeof(INT),
-    	    (compar_callback_fn)cmpUV) != NULL)
-    	return TRUE;
-	
-    return FALSE;
-}
-
-/*******************************************************************************
- *  	CheckMetrics
- *
- *  Check an AFMMETRICS structure to make sure all elements have been properly
- *  filled in.  (Don't check UV or L.)
- *
- */
-static const AFMMETRICS badMetrics =
-{
-    INT_MIN,	    	    	    	    	/* C */
-    INT_MIN,	    	    	    	    	/* UV */
-    FLT_MAX,	    	    	    	    	/* WX */
-    NULL,   	    	    	    	    	/* N */
-    { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }, 	/* B */
-    NULL    	    	    	    	    	/* L */
-};
-
-inline static BOOL CheckMetrics(const AFMMETRICS *metrics)
-{
-    if (    metrics->C	    == badMetrics.C  	||
-    	    metrics->WX     == badMetrics.WX  	||
-	    metrics->N	    == badMetrics.N    	||
-	    metrics->B.llx  == badMetrics.B.llx	||
-	    metrics->B.lly  == badMetrics.B.lly	||
-	    metrics->B.urx  == badMetrics.B.urx	||
-	    metrics->B.ury  == badMetrics.B.ury	)
-	return FALSE;
-	
-    return TRUE;
-}
-
-
-/***********************************************************
- *
- *	PSDRV_AFMGetCharMetrics
- *
- * Parses CharMetric section of AFM file.
- *
- * Actually only collects the widths of numbered chars and puts then in
- * afm->CharWidths.
- */
-static BOOL PSDRV_AFMGetCharMetrics(AFM *afm, FILE *fp)
-{
-    unsigned char line[256], valbuf[256];
-    unsigned char *cp, *item, *value, *curpos, *endpos;
-    int i;
-    AFMMETRICS *metric, *retval;
-
-    metric = HeapAlloc( PSDRV_Heap, 0, afm->NumofMetrics * sizeof(AFMMETRICS));
-    if (metric == NULL)
-        return FALSE;
-	
-    retval = metric;
-	
-    for(i = 0; i < afm->NumofMetrics; i++, metric++) {
-    
-    	*metric = badMetrics;
-
-	do {
-            if(!fgets(line, sizeof(line), fp)) {
-		ERR("Unexpected EOF\n");
-		HeapFree(PSDRV_Heap, 0, retval);
-		return FALSE;
-	    }
-	    cp = line + strlen(line);
-	    do {
-		*cp = '\0';
-		cp--;
-	    } while(cp >= line && isspace(*cp));
-	} while (!(*line));
-
-	curpos = line;
-	while(*curpos) {
-	    item = curpos;
-	    while(isspace(*item))
-	        item++;
-	    value = strpbrk(item, " \t");
-	    if (!value) {
-	    	ERR("No whitespace found.\n");
-		HeapFree(PSDRV_Heap, 0, retval);
-		return FALSE;
-	    }
-	    while(isspace(*value))
-	        value++;
-	    cp = endpos = strchr(value, ';');
-	    if (!cp) {
-	    	ERR("missing ;, failed. [%s]\n", line);
-		HeapFree(PSDRV_Heap, 0, retval);
-		return FALSE;
-	    }
-	    while(isspace(*--cp))
-	        ;
-	    memcpy(valbuf, value, cp - value + 1);
-	    valbuf[cp - value + 1] = '\0';
-	    value = valbuf;
-
-	    if(!strncmp(item, "C ", 2)) {
-	        value = strchr(item, ' ');
-		sscanf(value, " %d", &metric->C);
-
-	    } else if(!strncmp(item, "CH ", 3)) {
-	        value = strrchr(item, ' ');
-		sscanf(value, " %x", &metric->C);
-	    }
-
-	    else if(!strncmp("WX ", item, 3) || !strncmp("W0X ", item, 4)) {
-	        sscanf(value, "%f", &metric->WX);
-	        if(metric->C >= 0 && metric->C <= 0xff)
-		    afm->CharWidths[metric->C] = metric->WX;
-	    }
-
-	    else if(!strncmp("N ", item, 2)) {
-		metric->N = PSDRV_GlyphName(value);
-	    }
-
-	    else if(!strncmp("B ", item, 2)) {
-	        sscanf(value, "%f%f%f%f", &metric->B.llx, &metric->B.lly,
-				          &metric->B.urx, &metric->B.ury);
-
-		/* Store height of Aring to use as lfHeight */
-		if(metric->N && !strncmp(metric->N->sz, "Aring", 5))
-		    afm->FullAscender = metric->B.ury;
-	    }
-
-	    /* Ligatures go here... */
-
-	    curpos = endpos + 1;
-	}
-	
-	if (CheckMetrics(metric) == FALSE) {
-	    ERR("Error parsing character metrics\n");
-	    HeapFree(PSDRV_Heap, 0, retval);
-	    return FALSE;
-	}
-
-	TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n",
-	      metric->N->sz, metric->WX, metric->B.llx, metric->B.lly,
-	      metric->B.urx, metric->B.ury);
-    }
-    
-    SortFontMetrics(afm, retval);
-    
-    afm->Metrics = retval;
-
-    return TRUE;
-}
-
-
-/***********************************************************
- *
- *	PSDRV_AFMParse
- *
- * Fills out an AFM structure and associated substructures (see psdrv.h)
- * for a given AFM file. All memory is allocated from the driver heap. 
- * Returns a ptr to the AFM structure or NULL on error.
- *
- * This is not complete (we don't handle kerning yet) and not efficient
- */
-
-static const AFM *PSDRV_AFMParse(char const *file)
-{
-    FILE *fp;
-    unsigned char buf[256];
-    unsigned char *value;
-    AFM *afm;
-    unsigned char *cp;
-    int afmfile = 0; 
-    int c;
-    LPSTR font_name = NULL, full_name = NULL, family_name = NULL,
-    	    encoding_scheme = NULL;
-
-    TRACE("parsing '%s'\n", file);
-
-    if((fp = fopen(file, "r")) == NULL) {
-        MESSAGE("Can't open AFM file '%s'. Please check wine.conf .\n", file);
-        return NULL;
-    }
-
-    afm = HeapAlloc(PSDRV_Heap, 0, sizeof(AFM));
-    if(!afm) {
-        fclose(fp);
-        return NULL;
-    }
-
-    cp = buf; 
-    while ( ( c = fgetc ( fp ) ) != EOF ) {
-	*cp = c;
-	if ( *cp == '\r' || *cp == '\n' || cp - buf == sizeof(buf)-2 ) {
-	    if ( cp == buf ) 
-		continue;
-	    *(cp+1)='\0';
-	}
-	else {
-	    cp ++; 
-	    continue;
-	}
-      
-	cp = buf + strlen(buf);
-	do {
-	    *cp = '\0';
-	    cp--;
-	} while(cp > buf && isspace(*cp));
-
-	cp = buf; 
-
-	if ( afmfile == 0 && strncmp ( buf, "StartFontMetrics", 16 ) )
-	    break;
-	afmfile = 1; 
-
-        value = strchr(buf, ' ');
-	if(value)
-	    while(isspace(*value))
-	        value++;
-
-	if(!strncmp("FontName", buf, 8)) {
-	    if (!(afm->FontName = font_name = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1 )))
-		goto cleanup_fp;
-            strcpy( (char *)afm->FontName, value );
-	    continue;
-	}
-
-	if(!strncmp("FullName", buf, 8)) {
-	    if (!(afm->FullName = full_name = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1 )))
-		goto cleanup_fp;
-	    strcpy( (char *)afm->FullName, value );
-	    continue;
-	}
-
-	if(!strncmp("FamilyName", buf, 10)) {
-	    if (!(afm->FamilyName = family_name = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1 )))
-		goto cleanup_fp;
-	    strcpy( (char *)afm->FamilyName, value );
-	    continue;
-	}
-	
-	if(!strncmp("Weight", buf, 6)) {
-	    if(!strncmp("Roman", value, 5) || !strncmp("Medium", value, 6)
-	       || !strncmp("Book", value, 4) || !strncmp("Regular", value, 7)
-	       || !strncmp("Normal", value, 6))
-	        afm->Weight = FW_NORMAL;
-	    else if(!strncmp("Demi", value, 4))
-	        afm->Weight = FW_DEMIBOLD;
-	    else if(!strncmp("Bold", value, 4))
-	        afm->Weight = FW_BOLD;
-	    else if(!strncmp("Light", value, 5))
-	        afm->Weight = FW_LIGHT;
-	    else if(!strncmp("Black", value, 5))
-	        afm->Weight = FW_BLACK;
-	    else {
-		WARN("%s specifies unknown Weight '%s'; treating as Roman\n",
-		     file, value);
-	        afm->Weight = FW_NORMAL;
-	    }
-	    continue;
-	}
-
-	if(!strncmp("ItalicAngle", buf, 11)) {
-	    sscanf(value, "%f", &(afm->ItalicAngle));
-	    continue;
-	}
-
-	if(!strncmp("IsFixedPitch", buf, 12)) {
-	    if(!strncasecmp("false", value, 5))
-	        afm->IsFixedPitch = FALSE;
-	    else
-	        afm->IsFixedPitch = TRUE;
-	    continue;
-	}
-
-	if(!strncmp("FontBBox", buf, 8)) {
-	    sscanf(value, "%f %f %f %f", &(afm->FontBBox.llx), 
-		   &(afm->FontBBox.lly), &(afm->FontBBox.urx), 
-		   &(afm->FontBBox.ury) );
-	    continue;
-	}
-
-	if(!strncmp("UnderlinePosition", buf, 17)) {
-	    sscanf(value, "%f", &(afm->UnderlinePosition) );
-	    continue;
-	}
-
-	if(!strncmp("UnderlineThickness", buf, 18)) {
-	    sscanf(value, "%f", &(afm->UnderlineThickness) );
-	    continue;
-	}
-
-	if(!strncmp("CapHeight", buf, 9)) {
-	    sscanf(value, "%f", &(afm->CapHeight) );
-	    continue;
-	}
-
-	if(!strncmp("XHeight", buf, 7)) {
-	    sscanf(value, "%f", &(afm->XHeight) );
-	    continue;
-	}
-
-	if(!strncmp("Ascender", buf, 8)) {
-	    sscanf(value, "%f", &(afm->Ascender) );
-	    continue;
-	}
-
-	if(!strncmp("Descender", buf, 9)) {
-	    sscanf(value, "%f", &(afm->Descender) );
-	    continue;
-	}
-
-	if(!strncmp("StartCharMetrics", buf, 16)) {
-	    sscanf(value, "%d", &(afm->NumofMetrics) );
-	    if (PSDRV_AFMGetCharMetrics(afm, fp) == FALSE)
-	    	goto cleanup_fp;
-	    continue;
-	}
-
-	if(!strncmp("EncodingScheme", buf, 14)) {
-            if (!(afm->EncodingScheme = encoding_scheme = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1)))
-                goto cleanup_fp;
-            strcpy( (char *)afm->EncodingScheme, value );
-	    continue;
-	}
-
-    }
-    fclose(fp);
-
-    if (afmfile == 0) {
-	HeapFree ( PSDRV_Heap, 0, afm ); 
-	return NULL;
-    }
-
-    if(afm->FontName == NULL) {
-        WARN("%s contains no FontName.\n", file);
-	if (!(afm->FontName = font_name = HeapAlloc(PSDRV_Heap, 0, 7)))
-	    goto cleanup;
-	strcpy( (char *)afm->FontName, "nofont" );
-    }
-    if(afm->FullName == NULL)
-    {
-        if (!(afm->FullName = full_name = HeapAlloc(PSDRV_Heap, 0, strlen(afm->FontName)+1 )))
-            goto cleanup;
-        strcpy( (char *)afm->FullName, afm->FontName );
-    }
-    if(afm->FamilyName == NULL)
-    {
-        if (!(afm->FamilyName = family_name = HeapAlloc(PSDRV_Heap, 0, strlen(afm->FontName)+1 )))
-            goto cleanup;
-        strcpy( (char *)afm->FamilyName, afm->FontName );
-    }
-
-    if(afm->Ascender == 0.0)
-        afm->Ascender = afm->FontBBox.ury;
-    if(afm->Descender == 0.0)
-        afm->Descender = afm->FontBBox.lly;
-    if(afm->FullAscender == 0.0)
-        afm->FullAscender = afm->Ascender;
-    if(afm->Weight == 0)
-        afm->Weight = FW_NORMAL;
-	
-    CalcWindowsMetrics(afm);
-    
-    if (afm->EncodingScheme != NULL &&
-    	    strcmp(afm->EncodingScheme, "AdobeStandardEncoding") == 0)
-    	PSDRV_ReencodeCharWidths(afm);
-	
-    return afm;
-    
-    cleanup_fp:
-    
-    	fclose(fp);
-    
-    cleanup:
-    
-    	if (font_name == NULL)
-	    HeapFree(PSDRV_Heap, 0, font_name);
-	if (full_name == NULL)
-	    HeapFree(PSDRV_Heap, 0, full_name);
-	if (family_name == NULL)
-	    HeapFree(PSDRV_Heap, 0, family_name);
-	if (encoding_scheme == NULL)
-	    HeapFree(PSDRV_Heap, 0, encoding_scheme);
-	    
-	HeapFree(PSDRV_Heap, 0, afm);
-	    
-	return NULL;
-}
 
 /***********************************************************
  *
@@ -479,7 +50,7 @@
  * 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, char *name)
+const AFM *PSDRV_FindAFMinList(FONTFAMILY *head, LPCSTR name)
 {
     FONTFAMILY *family;
     AFMLISTENTRY *afmle;
@@ -564,40 +135,6 @@
     return TRUE;
 }
 
-/**********************************************************
- *
- *	PSDRV_ReencodeCharWidths
- *
- * Re map the CharWidths field of the afm to correspond to an ANSI encoding
- *
- */
-static void PSDRV_ReencodeCharWidths(AFM *afm)
-{
-    int i, j;
-    const AFMMETRICS *metric;
-
-    for(i = 0; i < 256; i++) {
-        if(isalnum(i))
-	    continue;
-	if(PSDRV_ANSIVector[i] == NULL) {
-	    afm->CharWidths[i] = 0.0;
-	    continue;
-	}
-        for (j = 0, metric = afm->Metrics; j < afm->NumofMetrics; j++, metric++) {
-	    if(metric->N && !strcmp(metric->N->sz, PSDRV_ANSIVector[i])) {
-	        afm->CharWidths[i] = metric->WX;
-		break;
-	    }
-	}
-	if(j == afm->NumofMetrics) {
-	    WARN("Couldn't find glyph '%s' in font '%s'\n",
-		 PSDRV_ANSIVector[i], afm->FontName);
-	    afm->CharWidths[i] = 0.0;
-	}
-    }
-    return;
-}
-
 
 /***********************************************************
  *
@@ -629,77 +166,6 @@
     return;
 }
 
-/*******************************************************************************
- *  SortFontMetrics
- *
- *  Initializes the UV member of each glyph's AFMMETRICS and sorts each font's
- *  Metrics by Unicode Value.  If the font has a standard encoding (i.e. it is
- *  using the Adobe Glyph List encoding vector), look up each glyph's Unicode
- *  Value based on it's glyph name.  If the font has a font-specific encoding,
- *  map the default PostScript encodings into the Unicode private use area.
- *
- */
-static int UnicodeGlyphByNameIndex(const UNICODEGLYPH *a, const UNICODEGLYPH *b)
-{
-    return a->name->index - b->name->index;
-}
-
-static int AFMMetricsByUV(const AFMMETRICS *a, const AFMMETRICS *b)
-{
-    return a->UV - b->UV;
-}
- 
-static VOID SortFontMetrics(AFM *afm, AFMMETRICS *metrics)
-{
-    INT     i;
-    
-    TRACE("%s\n", afm->FontName);
-    
-    if (strcmp(afm->EncodingScheme, "FontSpecific") != 0)
-    {
-    	PSDRV_IndexGlyphList();     	/* enable searching by name index */
-	    
-	for (i = 0; i < afm->NumofMetrics; ++i)
-	{
-	    UNICODEGLYPH    ug, *pug;
-		    
-	    ug.name = metrics[i].N;
-		    
-	    pug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
-	    	    sizeof(UNICODEGLYPH),
-		    (compar_callback_fn)UnicodeGlyphByNameIndex);
-	    if (pug == NULL)
-	    {
-	    	WARN("Glyph '%s' in font '%s' does not have a UV\n",
-    		    	ug.name->sz, afm->FullName);
-		metrics[i].UV = -1;
-	    }
-	    else
-	    {
-	    	metrics[i].UV = pug->UV;
-	    }
-	}
-    }
-    else    	    	    	    	    /* FontSpecific encoding */
-    {
-    	for (i = 0; i < afm->NumofMetrics; ++i)
-	    metrics[i].UV = metrics[i].C;
-    }
-	    
-    qsort(metrics, afm->NumofMetrics, sizeof(AFMMETRICS),
-    	    (compar_callback_fn)AFMMetricsByUV);
-		    
-    for (i = 0; i < afm->NumofMetrics; ++i) 	/* count unencoded glyphs */
-    	if (metrics[i].UV >= 0)
-	    break;
-	    
-    if (i != 0)
-    {
-    	TRACE("Ignoring %i unencoded glyphs\n", i);
-    	afm->NumofMetrics -= i;
-	memmove(metrics, metrics + i, afm->NumofMetrics * sizeof(*metrics));
-    }
-}
 
 /*******************************************************************************
  *  PSDRV_CalcAvgCharWidth
@@ -755,83 +221,6 @@
     return (SHORT)(w + 0.5);
 }
 
-/*******************************************************************************
- *  CalcWindowsMetrics
- *
- *  Calculates several Windows-specific font metrics for each font.
- *
- */
-static VOID CalcWindowsMetrics(AFM *afm)
-{
-    WINMETRICS	wm;
-    INT     	i;
-	    
-    wm.usUnitsPerEm = 1000;         	    	/* for PostScript fonts */
-    wm.sTypoAscender = (SHORT)(afm->Ascender + 0.5);
-    wm.sTypoDescender = (SHORT)(afm->Descender - 0.5);
-	    
-    wm.sTypoLineGap = 1200 - (wm.sTypoAscender - wm.sTypoDescender);
-    if (wm.sTypoLineGap < 0)
-    	wm.sTypoLineGap = 0;
-		
-    wm.usWinAscent = 0;
-    wm.usWinDescent = 0;
-	    
-    for (i = 0; i < afm->NumofMetrics; ++i)
-    {
-    	if (IsWinANSI(afm->Metrics[i].UV) == FALSE)
-	    continue;
-	    
-	if (afm->Metrics[i].B.ury > 0)
-	{
-	    USHORT ascent = (USHORT)(afm->Metrics[i].B.ury + 0.5);
-					
-	    if (ascent > wm.usWinAscent)
-	    	wm.usWinAscent = ascent;
-	}
-	
-	if (afm->Metrics[i].B.lly < 0)    
-	{
-	    USHORT descent = (USHORT)(-(afm->Metrics[i].B.lly) + 0.5);
-	    
-	    if (descent > wm.usWinDescent)
-	    	wm.usWinDescent = descent;
-	}
-    }
-    
-    if (wm.usWinAscent == 0 && afm->FontBBox.ury > 0)
-    	wm.usWinAscent = (USHORT)(afm->FontBBox.ury + 0.5);
-	
-    if (wm.usWinDescent == 0 && afm->FontBBox.lly < 0)
-    	wm.usWinDescent = (USHORT)(-(afm->FontBBox.lly) + 0.5);
-		
-    wm.sAscender = wm.usWinAscent;
-    wm.sDescender = -(wm.usWinDescent);
-	    
-    wm.sLineGap = 1150 - (wm.sAscender - wm.sDescender);
-    if (wm.sLineGap < 0)
-    	wm.sLineGap = 0;
-		
-    wm.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
-						
-    TRACE("Windows metrics for '%s':\n", afm->FullName);
-    TRACE("\tsAscender = %i\n", wm.sAscender);
-    TRACE("\tsDescender = %i\n", wm.sDescender);
-    TRACE("\tsLineGap = %i\n", wm.sLineGap);
-    TRACE("\tusUnitsPerEm = %u\n", wm.usUnitsPerEm);
-    TRACE("\tsTypoAscender = %i\n", wm.sTypoAscender);
-    TRACE("\tsTypoDescender = %i\n", wm.sTypoDescender);
-    TRACE("\tsTypoLineGap = %i\n", wm.sTypoLineGap);
-    TRACE("\tusWinAscent = %u\n", wm.usWinAscent);
-    TRACE("\tusWinDescent = %u\n", wm.usWinDescent);
-    TRACE("\tsAvgCharWidth = %i\n", wm.sAvgCharWidth);
-	    
-    afm->WinMetrics = wm;
-	    
-    /* See afm2c.c and mkagl.c for an explanation of this */
-    /*	PSDRV_AFM2C(afm);   */
-}
-
 
 /*******************************************************************************
  *  AddBuiltinAFMs
@@ -863,129 +252,33 @@
  *
  *	PSDRV_GetFontMetrics
  *
- * Parses all afm files listed in [afmfiles] and [afmdirs] of wine.conf
+ * 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.
  */
-
-static BOOL PSDRV_ReadAFMDir(const char* afmdir) {
-    DIR *dir;
-    const AFM	*afm;
-    BOOL added;
-
-    dir = opendir(afmdir);
-    if (dir) {
-	struct dirent *dent;
-	while ((dent=readdir(dir))) {
-	    if (strstr(dent->d_name,".afm")) {
-		char *afmfn;
-
-		afmfn=(char*)HeapAlloc(PSDRV_Heap,0, 
-		    	strlen(afmdir)+strlen(dent->d_name)+2);
-		if (afmfn == NULL) {
-		    closedir(dir);
-		    return FALSE;
-		}
-		strcpy(afmfn,afmdir);
-		strcat(afmfn,"/");
-		strcat(afmfn,dent->d_name);
-		TRACE("loading AFM %s\n",afmfn);
-		afm = PSDRV_AFMParse(afmfn);
-		if (afm) {
-		    if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added) == FALSE) {
-		    	closedir(dir);
-			return FALSE;
-		    }
-		}
-		else {
-		    WARN("Error parsing %s\n", afmfn);
-		}
-		HeapFree(PSDRV_Heap,0,afmfn);
-	    }
-	}
-	closedir(dir);
-    }
-    else {
-    	WARN("Error opening %s\n", afmdir);
-    }
-    
-    return TRUE;
-}
-
+ 
 BOOL PSDRV_GetFontMetrics(void)
 {
-    int idx;
-    char key[256];
-    char value[256];
-    HKEY hkey;
-    DWORD type, key_len, value_len;
-    BOOL added;
-
     if (PSDRV_GlyphListInit() != 0)
-	return FALSE;
-
-    if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\afmfiles",
-		     0, KEY_READ, &hkey))
-	goto no_afmfiles;
-
-    idx = 0;
-    key_len = sizeof(key);
-    value_len = sizeof(value);
-    while(!RegEnumValueA(hkey, idx++, key, &key_len, NULL, &type, value, &value_len))
-    {
-        const AFM* afm = PSDRV_AFMParse(value);
+    	return FALSE;
 	
-        if (afm) {
-            if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added) == FALSE) {
-		RegCloseKey(hkey);
-	    	return FALSE;
-	    }
-        }
-	else {
-	    WARN("Error parsing %s\n", value);
-	}
-
-	/* initialize lengths for new iteration */
-	key_len = sizeof(key);
-	value_len = sizeof(value);
-    }
-    RegCloseKey(hkey);
-
-no_afmfiles:
-
-    if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\afmdirs",
-		     0, KEY_READ, &hkey))
-	goto no_afmdirs;
-
-    idx = 0;
-    key_len = sizeof(key);
-    value_len = sizeof(value);
-    while(!RegEnumValueA(hkey, idx++, key, &key_len, NULL, &type, value, &value_len))
-    {
-	if (PSDRV_ReadAFMDir (value) == FALSE)
-	{
-	    RegCloseKey(hkey);
-	    return FALSE;
-	}
-
-	/* initialize lengths for new iteration */
-	key_len = sizeof(key);
-	value_len = sizeof(value);
-    }
-    RegCloseKey(hkey);
-
-no_afmdirs:
-
-    if (AddBuiltinAFMs() == FALSE)
+    if (PSDRV_GetType1Metrics() == FALSE)
     	return FALSE;
 
-#ifdef HAVE_FREETYPE   
+#ifdef HAVE_FREETYPE
     if (PSDRV_GetTrueTypeMetrics() == FALSE)
     	return FALSE;
-    PSDRV_IndexGlyphList();
 #endif
 
+    if (AddBuiltinAFMs() == FALSE)
+    	return FALSE;
+	
+    PSDRV_IndexGlyphList(); 	    /* Enable fast searching of glyph names */
+    
     PSDRV_DumpFontList();
+    
     return TRUE;
 }
diff -urN ../wine-20010726cvs/dlls/wineps/psdrv.h ./dlls/wineps/psdrv.h
--- ../wine-20010726cvs/dlls/wineps/psdrv.h	Thu Jul 26 20:33:10 2001
+++ ./dlls/wineps/psdrv.h	Wed Aug  8 00:22:08 2001
@@ -297,7 +297,7 @@
 extern BOOL PSDRV_GetFontMetrics(void);
 extern PPD *PSDRV_ParsePPD(char *fname);
 extern PRINTERINFO *PSDRV_FindPrinterInfo(LPCSTR name);
-extern const AFM *PSDRV_FindAFMinList(FONTFAMILY *head, char *name);
+extern const AFM *PSDRV_FindAFMinList(FONTFAMILY *head, LPCSTR name);
 extern BOOL PSDRV_AddAFMtoList(FONTFAMILY **head, const AFM *afm,
     	BOOL *p_added);
 extern void PSDRV_FreeAFMList( FONTFAMILY *head );
@@ -425,10 +425,11 @@
 				      WORD fwCapability, LPSTR lpszOutput,
 				      LPDEVMODEA lpdm);
 VOID PSDRV_DrawLine( DC *dc );
-INT PSDRV_GlyphListInit();
+INT PSDRV_GlyphListInit(void);
 const GLYPHNAME *PSDRV_GlyphName(LPCSTR szName);
-VOID PSDRV_IndexGlyphList();
-BOOL PSDRV_GetTrueTypeMetrics();
+VOID PSDRV_IndexGlyphList(void);
+BOOL PSDRV_GetTrueTypeMetrics(void);
+BOOL PSDRV_GetType1Metrics(void);
 const AFMMETRICS *PSDRV_UVMetrics(LONG UV, const AFM *afm);
 SHORT PSDRV_CalcAvgCharWidth(const AFM *afm);
 
diff -urN ../wine-20010726cvs/dlls/wineps/truetype.c ./dlls/wineps/truetype.c
--- ../wine-20010726cvs/dlls/wineps/truetype.c	Thu Jul 26 20:33:10 2001
+++ ./dlls/wineps/truetype.c	Wed Aug  8 00:22:08 2001
@@ -577,7 +577,7 @@
     HKEY    	hkey;
     DWORD   	type, name_len, value_len;
 
-    if(RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+    if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
     	    "Software\\Wine\\Wine\\Config\\TrueType Font Directories",
 	    0, KEY_READ, &hkey) != ERROR_SUCCESS)
 	return TRUE;
@@ -593,7 +593,7 @@
     name_len = sizeof(name_buf);
     value_len = sizeof(value_buf);
     
-    while(RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf,
+    while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf,
     	    &value_len) == ERROR_SUCCESS)
     {
     	value_buf[sizeof(value_buf) - 1] = '\0';
diff -urN ../wine-20010726cvs/dlls/wineps/type1afm.c ./dlls/wineps/type1afm.c
--- ../wine-20010726cvs/dlls/wineps/type1afm.c	Wed Dec 31 18:00:00 1969
+++ ./dlls/wineps/type1afm.c	Wed Aug  8 13:44:04 2001
@@ -0,0 +1,1201 @@
+/*******************************************************************************
+ *  Adobe Font Metric (AFM) file parsing finctions for Wine PostScript driver.
+ *  See http://partners.adobe.com/asn/developer/pdfs/tn/5004.AFM_Spec.pdf.
+ *
+ *  Copyright 2001  Ian Pilcher
+ *
+ *
+ *  NOTE:  Many of the functions in this file can return either fatal errors
+ *  	(memory allocation failure) or non-fatal errors (unusable AFM file).
+ *  	Fatal errors are indicated by returning FALSE; see individual function
+ *  	descriptions for how they indicate non-fatal errors.
+ *
+ */
+ 
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h> 	    /* INT_MIN */
+
+#ifdef HAVE_FLOAT_H
+#include <float.h>  	    /* FLT_MAX */
+#endif
+
+#include "winnt.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "psdrv.h"
+#include "debugtools.h"
+
+DEFAULT_DEBUG_CHANNEL(psdrv);
+
+/*******************************************************************************
+ *  ReadLine
+ *
+ *  Reads a line from a text file into the buffer and trims trailing whitespace.
+ *  Can handle DOS and Unix text files, including weird DOS EOF.  Returns FALSE
+ *  for unexpected I/O errors; otherwise returns TRUE and sets *p_result to
+ *  either the number of characters in the returned string or one of the
+ *  following:
+ *
+ *  	0:  	    Blank (or all whitespace) line.  This is just a special case
+ *  	    	    of the normal behavior.
+ *
+ *  	EOF:	    End of file has been reached.
+ *
+ *  	INT_MIN:    Buffer overflow.  Returned string is truncated (duh!) and
+ *  	    	    trailing whitespace is *not* trimmed.  Remaining text in
+ *  	    	    line is discarded.  (I.e. the file pointer is positioned at
+ *  	    	    the beginning of the next line.)
+ *
+ */
+static BOOL ReadLine(FILE *file, CHAR buffer[], INT bufsize, INT *p_result)
+{
+     CHAR   *cp;
+     INT    i;
+     
+     if (fgets(buffer, bufsize, file) == NULL)
+     {
+     	if (feof(file) == 0)	    	    	    	/* EOF or error? */
+	{
+	    ERR("%s\n", strerror(errno));
+    	    return FALSE;
+	}
+	
+	*p_result = EOF;
+	return TRUE;
+    }
+    
+    cp = strchr(buffer, '\n');
+    if (cp == NULL)
+    {
+    	i = strlen(buffer);
+	
+	if (i == bufsize - 1)	    /* max possible; was line truncated? */
+	{
+	    do
+	    	i = fgetc(file);    	    	/* find the newline or EOF */
+	    while (i != '\n' && i != EOF);
+	    
+	    if (i == EOF)
+	    {
+	    	if (feof(file) == 0)	    	    	/* EOF or error? */
+		{
+		    ERR("%s\n", strerror(errno));
+		    return FALSE;
+		}
+		
+		WARN("No newline at EOF\n");
+	    }
+	    
+	    *p_result = INT_MIN;
+	    return TRUE;
+	}
+	else	    	    	    	/* no newline and not truncated */
+	{
+	    if (strcmp(buffer, "\x1a") == 0)	    /* test for DOS EOF */
+	    {
+	    	*p_result = EOF;
+		return TRUE;
+	    }
+	    
+	    WARN("No newline at EOF\n");
+	    cp = buffer + i;	/* points to \0 where \n should have been */
+	}
+    }
+    
+    do
+    {
+    	*cp = '\0'; 	    	    	    	/* trim trailing whitespace */
+	if (cp == buffer)
+	    break;  	    	    	    	/* don't underflow buffer */
+	--cp;
+    }
+    while (isspace(*cp));
+    
+    *p_result = strlen(buffer);
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  FindLine
+ *
+ *  Finds a line in the file that begins with the given string.  Returns FALSE
+ *  for unexpected I/O errors; returns an empty (zero character) string if the
+ *  requested line is not found.
+ *
+ *  NOTE:  The file pointer *MUST* be positioned at the beginning of a line when
+ *  	this function is called.  Otherwise, an infinite loop can result.
+ *
+ */
+static BOOL FindLine(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key)
+{
+    INT     len = strlen(key);
+    LONG    start = ftell(file);
+    
+    do
+    {
+	INT 	result;
+	BOOL	ok;
+	
+	ok = ReadLine(file, buffer, bufsize, &result);
+	if (ok == FALSE)
+	    return FALSE;
+	    
+	if (result > 0 && strncmp(buffer, key, len) == 0)
+	    return TRUE;
+	    
+	if (result == EOF)
+	{
+	    rewind(file);
+	}
+	else if (result == INT_MIN)
+	{
+	    WARN("Line beginning '%32s...' is too long; ignoring\n", buffer);
+	}
+    }
+    while (ftell(file) != start);
+    
+    WARN("Couldn't find line '%s...' in AFM file\n", key);
+    buffer[0] = '\0';
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  DoubleToFloat
+ *
+ *  Utility function to convert double to float while checking for overflow.
+ *  Will also catch strtod overflows, since HUGE_VAL > FLT_MAX (at least on
+ *  Linux x86/gcc).
+ *
+ */
+inline static BOOL DoubleToFloat(float *p_f, double d)
+{
+    if (d > (double)FLT_MAX || d < -(double)FLT_MAX)
+    	return FALSE;
+	
+    *p_f = (float)d;
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  Round
+ *
+ *  Utility function to add or subtract 0.5 before converting to integer type.
+ *
+ */
+inline static float Round(float f)
+{
+    return (f >= 0.0) ? (f + 0.5) : (f - 0.5);
+}
+
+/*******************************************************************************
+ *  ReadFloat
+ *
+ *  Finds and parses a line of the form '<key> <value>', where value is a
+ *  number.  Sets *p_found to FALSE if a corresponding line cannot be found, or
+ *  it cannot be parsed; also sets *p_ret to 0.0, so calling functions can just
+ *  skip the check of *p_found if the item is not required.
+ *
+ */
+static BOOL ReadFloat(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
+    	FLOAT *p_ret, BOOL *p_found)
+{
+    CHAR    *cp, *end_ptr;
+    double  d;
+
+    if (FindLine(file, buffer, bufsize, key) == FALSE)
+    	return FALSE;
+	
+    if (buffer[0] == '\0')  	    /* line not found */
+    {
+    	*p_found = FALSE;
+	*p_ret = 0.0;
+	return TRUE;
+    }
+    
+    cp = buffer + strlen(key);	    	    	    /* first char after key */
+    errno = 0;
+    d = strtod(cp, &end_ptr);
+    
+    if (end_ptr == cp || errno != 0 || DoubleToFloat(p_ret, d) == FALSE)
+    {
+    	WARN("Error parsing line '%s'\n", buffer); 
+    	*p_found = FALSE;
+	*p_ret = 0.0;
+	return TRUE;
+    }
+    
+    *p_found = TRUE;
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  ReadInt
+ *
+ *  See description of ReadFloat.
+ *
+ */
+static BOOL ReadInt(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
+    	INT *p_ret, BOOL *p_found)
+{
+    BOOL    retval;
+    FLOAT   f;
+
+    retval = ReadFloat(file, buffer, bufsize, key, &f, p_found);
+    if (retval == FALSE || *p_found == FALSE)
+    {
+    	*p_ret = 0;
+    	return retval;
+    }
+    
+    f = Round(f);
+    
+    if (f > (FLOAT)INT_MAX || f < (FLOAT)INT_MIN)
+    {
+    	WARN("Error parsing line '%s'\n", buffer);
+    	*p_ret = 0;
+	*p_found = FALSE;
+	return TRUE;
+    }
+    
+    *p_ret = (INT)f;
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  ReadString
+ *
+ *  Returns FALSE on I/O error or memory allocation failure; sets *p_str to NULL
+ *  if line cannot be found or can't be parsed.
+ *
+ */
+static BOOL ReadString(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
+    	LPSTR *p_str)
+{
+    CHAR    *cp;
+
+    if (FindLine(file, buffer, bufsize, key) == FALSE)
+    	return FALSE;
+	
+    if (buffer[0] == '\0')
+    {
+    	*p_str = NULL;
+	return TRUE;
+    }
+    
+    cp = buffer + strlen(key);	    	    	    /* first char after key */
+    if (*cp == '\0')
+    {
+    	*p_str = NULL;
+	return TRUE;
+    }
+    
+    while (isspace(*cp))    	    	/* find first non-whitespace char */
+    	++cp;
+    
+    *p_str = HeapAlloc(PSDRV_Heap, 0, strlen(cp) + 1);
+    if (*p_str == NULL)
+    	return FALSE;
+	
+    strcpy(*p_str, cp);
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  ReadBBox
+ *
+ *  Similar to ReadFloat above.
+ *
+ */
+static BOOL ReadBBox(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
+    	BOOL *p_found)
+{
+    CHAR    *cp, *end_ptr;
+    double  d;
+
+    if (FindLine(file, buffer, bufsize, "FontBBox") == FALSE)
+    	return FALSE;
+	
+    if (buffer[0] == '\0')
+    {
+    	*p_found = FALSE;
+	return TRUE;
+    }
+    
+    errno = 0;
+    
+    cp = buffer + sizeof("FontBBox");
+    d = strtod(cp, &end_ptr);
+    if (end_ptr == cp || errno != 0 ||
+    	    DoubleToFloat(&(afm->FontBBox.llx), d) == FALSE)
+    	goto parse_error;
+
+    cp = end_ptr;
+    d = strtod(cp, &end_ptr);
+    if (end_ptr == cp || errno != 0 ||
+    	    DoubleToFloat(&(afm->FontBBox.lly), d) == FALSE)
+    	goto parse_error;
+	
+    cp = end_ptr;
+    d = strtod(cp, &end_ptr);
+    if (end_ptr == cp || errno != 0
+    	    || DoubleToFloat(&(afm->FontBBox.urx), d) == FALSE)
+    	goto parse_error;
+	
+    cp = end_ptr;
+    d = strtod(cp, &end_ptr);
+    if (end_ptr == cp || errno != 0
+    	    || DoubleToFloat(&(afm->FontBBox.ury), d) == FALSE)
+    	goto parse_error;
+	
+    *p_found = TRUE;
+    return TRUE;
+    
+    parse_error:
+    	WARN("Error parsing line '%s'\n", buffer);
+	*p_found = FALSE;
+	return TRUE;
+}
+
+/*******************************************************************************
+ *  ReadWeight
+ *
+ *  Finds and parses the 'Weight' line of an AFM file.  Only tries to determine
+ *  if a font is bold (FW_BOLD) or not (FW_NORMAL) -- ignoring all those cute
+ *  little FW_* typedefs in the Win32 doc.  AFAICT, this is what the Windows
+ *  PostScript driver does.
+ *
+ */
+static const struct { LPCSTR keyword; INT weight; } afm_weights[] =
+{
+    { "REGULAR",	FW_NORMAL },
+    { "ROMAN",	    	FW_NORMAL },
+    { "BOLD",	    	FW_BOLD },
+    { "BOOK",	    	FW_NORMAL },
+    { "MEDIUM",     	FW_NORMAL },
+    { "LIGHT",	    	FW_NORMAL },
+    { "BLACK",	    	FW_BOLD },
+    { "HEAVY",	    	FW_BOLD },
+    { "DEMI",	    	FW_BOLD },
+    { "ULTRA",	    	FW_BOLD },
+    { "SUPER" ,     	FW_BOLD },
+    { NULL, 	    	0 }
+};
+ 
+static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
+    	BOOL *p_found)
+{
+    LPSTR   sz;
+    CHAR    *cp;
+    INT     i;
+    
+    if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE)
+    	return FALSE;
+
+    if (sz == NULL)
+    {
+    	*p_found = FALSE;
+	return TRUE;
+    }
+	
+    for (cp = sz; *cp != '\0'; ++cp)
+    	*cp = toupper(*cp);
+	
+    for (i = 0; afm_weights[i].keyword != NULL; ++i)
+    {
+    	if (strstr(sz, afm_weights[i].keyword) != NULL)
+	{
+	    afm->Weight = afm_weights[i].weight;
+	    *p_found = TRUE;
+	    HeapFree(PSDRV_Heap, 0, sz);
+	    return TRUE;
+	}
+    }
+    
+    WARN("Unknown weight '%s'; treating as Roman\n", sz);
+    
+    afm->Weight = FW_NORMAL;
+    *p_found = TRUE;
+    HeapFree(PSDRV_Heap, 0, sz);
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  ReadFixedPitch
+ *
+ */
+static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
+    	BOOL *p_found)
+{
+    LPSTR   sz;
+    
+    if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE)
+    	return FALSE;
+	
+    if (sz == NULL)
+    {
+    	*p_found = FALSE;
+	return TRUE;
+    }
+
+    if (strcasecmp(sz, "false") == 0)
+    {
+    	afm->IsFixedPitch = FALSE;
+	*p_found = TRUE;
+	HeapFree(PSDRV_Heap, 0, sz);
+	return TRUE;
+    }
+    
+    if (strcasecmp(sz, "true") == 0)
+    {
+    	afm->IsFixedPitch = TRUE;
+	*p_found = TRUE;
+	HeapFree(PSDRV_Heap, 0, sz);
+	return TRUE;
+    }
+    
+    WARN("Can't parse line '%s'\n", buffer);
+    
+    *p_found = FALSE;
+    HeapFree(PSDRV_Heap, 0, sz);
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  ReadFontMetrics
+ *
+ *  Allocates space for the AFM on the driver heap and reads basic font metrics.
+ *  Returns FALSE for memory allocation failure; sets *p_afm to NULL if AFM file
+ *  is unusable.
+ *
+ */
+static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm)
+{
+    AFM     *afm;
+    BOOL    retval, found;
+
+    *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
+    if (afm == NULL)
+    	return FALSE;
+	
+    retval = ReadWeight(file, buffer, bufsize, afm, &found);
+    if (retval == FALSE || found == FALSE)
+    	goto cleanup_afm;
+	
+    retval = ReadFloat(file, buffer, bufsize, "ItalicAngle",
+    	    &(afm->ItalicAngle), &found);
+    if (retval == FALSE || found == FALSE)
+    	goto cleanup_afm;
+	
+    retval = ReadFixedPitch(file, buffer, bufsize, afm, &found);
+    if (retval == FALSE || found == FALSE)
+    	goto cleanup_afm;
+	
+    retval = ReadBBox(file, buffer, bufsize, afm, &found);
+    if (retval == FALSE || found == FALSE)
+    	goto cleanup_afm;
+	
+    retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition",
+    	    &(afm->UnderlinePosition), &found);
+    if (retval == FALSE || found == FALSE)
+    	goto cleanup_afm;
+	
+    retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness",
+    	    &(afm->UnderlineThickness), &found);
+    if (retval == FALSE || found == FALSE)
+    	goto cleanup_afm;
+	
+    retval = ReadFloat(file, buffer, bufsize, "CapHeight",   	/* optional */
+    	    &(afm->CapHeight), &found);
+    if (retval == FALSE)
+    	goto cleanup_afm;
+	
+    retval = ReadFloat(file, buffer, bufsize, "XHeight",     	/* optional */
+    	    &(afm->XHeight), &found);
+    if (retval == FALSE)
+    	goto cleanup_afm;
+	
+    retval = ReadFloat(file, buffer, bufsize, "Ascender",    	/* optional */
+    	    &(afm->Ascender), &found);
+    if (retval == FALSE)
+    	goto cleanup_afm;
+	
+    retval = ReadFloat(file, buffer, bufsize, "Descender",   	/* optional */
+    	    &(afm->Descender), &found);
+    if (retval == FALSE)
+    	goto cleanup_afm;
+	
+    afm->WinMetrics.usUnitsPerEm = 1000;
+    afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender);
+    afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender);
+    
+    if (afm->WinMetrics.sTypoAscender == 0)
+    	afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury);
+	
+    if (afm->WinMetrics.sTypoDescender == 0)
+    	afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly);
+	
+    afm->WinMetrics.sTypoLineGap = 1200 -
+    	    (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender);
+    if (afm->WinMetrics.sTypoLineGap < 0)
+    	afm->WinMetrics.sTypoLineGap = 0;
+	
+    return TRUE;
+	
+    cleanup_afm:    	    	    	/* handle fatal or non-fatal errors */
+    	HeapFree(PSDRV_Heap, 0, afm);
+	*p_afm = NULL;
+	return retval;
+}
+
+/*******************************************************************************
+ *  ParseC
+ *
+ *  Fatal error:    	return FALSE (none defined)
+ *
+ *  Non-fatal error:	leave metrics->C set to INT_MAX
+ *
+ */
+static BOOL ParseC(LPSTR sz, AFMMETRICS *metrics)
+{
+    int     base = 10;
+    long    l;
+    CHAR    *cp, *end_ptr;
+
+    cp = sz + 1;
+    
+    if (*cp == 'H')
+    {
+    	base = 16;
+	++cp;
+    }
+    
+    errno = 0;
+    l = strtol(cp, &end_ptr, base);
+    if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN)
+    {
+    	WARN("Error parsing character code '%s'\n", sz);
+	return TRUE;
+    }
+    
+    metrics->C = (INT)l;
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  ParseW
+ *
+ *  Fatal error:    	return FALSE (none defined)
+ *
+ *  Non-fatal error:	leave metrics->WX set to FLT_MAX
+ *
+ */
+static BOOL ParseW(LPSTR sz, AFMMETRICS *metrics)
+{
+    CHAR    *cp, *end_ptr;
+    BOOL    vector = TRUE;
+    double  d;
+
+    cp = sz + 1;
+    
+    if (*cp == '0')
+    	++cp;
+	
+    if (*cp == 'X')
+    {
+    	vector = FALSE;
+	++cp;
+    }
+    
+    if (!isspace(*cp))
+    	goto parse_error;
+	
+    errno = 0;
+    d = strtod(cp, &end_ptr);
+    if (end_ptr == cp || errno != 0 ||
+    	    DoubleToFloat(&(metrics->WX), d) == FALSE)
+    	goto parse_error;
+	
+    if (vector == FALSE)
+    	return TRUE;
+	
+    /*	Make sure that Y component of vector is zero */
+	
+    d = strtod(cp, &end_ptr);	    	    	    	    /* errno == 0 */
+    if (end_ptr == cp || errno != 0 || d != 0.0)
+    {
+    	metrics->WX = FLT_MAX;
+    	goto parse_error;
+    }
+	
+    return TRUE;
+	
+    parse_error:
+    	WARN("Error parsing character width '%s'\n", sz);
+	return TRUE;
+}
+
+/*******************************************************************************
+ *
+ *  ParseB
+ *
+ *  Fatal error:    	return FALSE (none defined)
+ *
+ *  Non-fatal error:	leave metrics->B.ury set to FLT_MAX
+ *
+ */
+static BOOL ParseB(LPSTR sz, AFMMETRICS *metrics)
+{
+    CHAR    *cp, *end_ptr;
+    double  d;
+    
+    errno = 0;
+    
+    cp = sz + 1;
+    d = strtod(cp, &end_ptr);
+    if (end_ptr == cp || errno != 0 ||
+    	    DoubleToFloat(&(metrics->B.llx), d) == FALSE)
+	goto parse_error;
+	
+    cp = end_ptr;
+    d = strtod(cp, &end_ptr);
+    if (end_ptr == cp || errno != 0 ||
+    	    DoubleToFloat(&(metrics->B.lly), d) == FALSE)
+	goto parse_error;
+	
+    cp = end_ptr;
+    d = strtod(cp, &end_ptr);
+    if (end_ptr == cp || errno != 0 ||
+    	    DoubleToFloat(&(metrics->B.urx), d) == FALSE)
+	goto parse_error;
+
+    cp = end_ptr;
+    d = strtod(cp, &end_ptr);
+    if (end_ptr == cp || errno != 0 ||
+    	    DoubleToFloat(&(metrics->B.ury), d) == FALSE)
+	goto parse_error;
+
+    return TRUE;
+    
+    parse_error:
+    	WARN("Error parsing glyph bounding box '%s'\n", sz);
+	return TRUE;
+}
+
+/*******************************************************************************
+ *  ParseN
+ *
+ *  Fatal error:    	return FALSE (PSDRV_GlyphName failure)
+ *
+ *  Non-fatal error:	leave metrics-> set to NULL
+ *
+ */
+static BOOL ParseN(LPSTR sz, AFMMETRICS *metrics)
+{
+    CHAR    save, *cp, *end_ptr;
+    
+    cp = sz + 1;
+    
+    while (isspace(*cp))
+    	++cp;
+	
+    end_ptr = cp;
+    
+    while (*end_ptr != '\0' && !isspace(*end_ptr))
+    	++end_ptr;
+	
+    if (end_ptr == cp)
+    {
+    	WARN("Error parsing glyph name '%s'\n", sz);
+	return TRUE;
+    }
+	
+    save = *end_ptr;
+    *end_ptr = '\0';
+    
+    metrics->N = PSDRV_GlyphName(cp);
+    if (metrics->N == NULL)
+    	return FALSE;
+	
+    *end_ptr = save;
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  ParseCharMetrics
+ *
+ *  Parses the metrics line for a single glyph in an AFM file.  Returns FALSE on
+ *  fatal error; sets *metrics to 'badmetrics' on non-fatal error.
+ *
+ */
+static const AFMMETRICS badmetrics =
+{
+    INT_MAX,	    	    	    	    	    /* C */
+    LONG_MAX,	    	    	    	    	    /* UV */
+    FLT_MAX,	    	    	    	    	    /* WX */
+    NULL,   	    	    	    	    	    /* N */
+    { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }, 	    /* B */
+    NULL    	    	    	    	    	    /* L */
+};
+ 
+static BOOL ParseCharMetrics(LPSTR buffer, INT len, AFMMETRICS *metrics)
+{
+    CHAR    *cp = buffer;
+
+    *metrics = badmetrics;
+    
+    while (*cp != '\0')
+    {
+    	while (isspace(*cp))
+	    ++cp;
+	    
+	switch(*cp)
+	{
+	    case 'C':	if (ParseC(cp, metrics) == FALSE)
+	    	    	    return FALSE;
+	    	    	break;
+			
+	    case 'W':	if (ParseW(cp, metrics) == FALSE)
+	    	    	    return FALSE;
+	    	    	break;
+			
+	    case 'N':	if (ParseN(cp, metrics) == FALSE)
+	    	    	    return FALSE;
+	    	    	break;
+			
+	    case 'B':	if (ParseB(cp, metrics) == FALSE)
+	    	    	    return FALSE;
+	    	    	break;
+	}
+	
+	cp = strchr(cp, ';');
+	if (cp == NULL)
+	{
+	    WARN("No terminating semicolon\n");
+	    break;
+	}
+	
+	++cp;
+    }
+    
+    if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL ||
+    	    metrics->B.ury == FLT_MAX)
+    {
+    	*metrics = badmetrics;
+	return TRUE;
+    }
+
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  IsWinANSI
+ *
+ *  Checks whether Unicode value is part of Microsoft code page 1252
+ *
+ */
+static const LONG ansiChars[21] =
+{
+    0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
+    0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
+    0x20ac, 0x2122, 0x2219
+};
+
+static int cmpUV(const void *a, const void *b)
+{
+    return (int)(*((const LONG *)a) - *((const LONG *)b));
+}
+ 
+inline static BOOL IsWinANSI(LONG uv)
+{
+    if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) ||
+    	    (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) ||
+	    (0x2020 <= uv && uv <= 2022))
+    	return TRUE;
+	
+    if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL)
+    	return TRUE;
+	
+    return FALSE;
+}
+
+/*******************************************************************************
+ *  Unicodify
+ *
+ *  Determines Unicode value (UV) for each glyph, based on font encoding.
+ *
+ *  	FontSpecific:	Usable encodings (0x20 - 0xff) are mapped into the
+ *  	    	    	Unicode private use range U+F020 - U+F0FF.
+ *
+ *  	other:	    	UV determined by glyph name, based on Adobe Glyph List.
+ *
+ *  Also does some font metric calculations that require UVs to be known.
+ *
+ */
+static int UnicodeGlyphByNameIndex(const void *a, const void *b)
+{
+    return ((const UNICODEGLYPH *)a)->name->index -
+    	    ((const UNICODEGLYPH *)b)->name->index;
+}
+ 
+static VOID Unicodify(AFM *afm, AFMMETRICS *metrics)
+{
+    INT     i;
+    
+    if (strcmp(afm->EncodingScheme, "FontSpecific") == 0)
+    {
+    	for (i = 0; i < afm->NumofMetrics; ++i)
+	{
+	    if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff)
+	    {
+	    	metrics[i].UV = ((LONG)(metrics[i].C)) | 0xf000L;
+	    }
+	    else
+	    {
+	    	TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz);
+		metrics[i].UV = -1L;
+	    }
+	}
+	
+	afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
+	afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
+    }
+    else    	    	    	    	    	/* non-FontSpecific encoding */
+    {
+    	UNICODEGLYPH	ug, *p_ug;
+	
+	PSDRV_IndexGlyphList();     	/* for fast searching of glyph names */
+	
+	afm->WinMetrics.sAscender = afm->WinMetrics.sDescender = 0;
+	
+	for (i = 0; i < afm->NumofMetrics; ++i)
+	{
+	    ug.name = metrics[i].N;
+	    p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
+	    	    sizeof(ug), UnicodeGlyphByNameIndex);
+	    if (p_ug == NULL)
+	    {
+	    	TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz);
+		metrics[i].UV = -1L;
+	    }
+	    else
+	    {
+	    	metrics[i].UV = p_ug->UV;
+		
+		if (IsWinANSI(p_ug->UV))
+		{
+		    SHORT   ury = (SHORT)Round(metrics[i].B.ury);
+		    SHORT   lly = (SHORT)Round(metrics[i].B.lly);
+		    
+		    if (ury > afm->WinMetrics.sAscender)
+		    	afm->WinMetrics.sAscender = ury;
+		    if (lly < afm->WinMetrics.sDescender)
+		    	afm->WinMetrics.sDescender = lly;
+		}
+	    }
+	}
+	
+	if (afm->WinMetrics.sAscender == 0)
+	    afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
+	if (afm->WinMetrics.sDescender == 0)
+	    afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
+    }
+    
+    afm->WinMetrics.sLineGap =
+    	    1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender);
+    if (afm->WinMetrics.sLineGap < 0)
+    	afm->WinMetrics.sLineGap = 0;
+    
+    afm->WinMetrics.usWinAscent = (afm->WinMetrics.sAscender > 0) ?
+    	    afm->WinMetrics.sAscender : 0;
+    afm->WinMetrics.usWinDescent = (afm->WinMetrics.sDescender < 0) ?
+    	    -(afm->WinMetrics.sDescender) : 0;
+}
+
+/*******************************************************************************
+ *  ReadCharMetrics
+ *
+ *  Reads metrics for all glyphs.  *p_metrics will be NULL on non-fatal error.
+ *
+ */
+static int AFMMetricsByUV(const void *a, const void *b)
+{
+    return ((const AFMMETRICS *)a)->UV - ((const AFMMETRICS *)b)->UV;
+} 
+ 
+static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
+    	AFMMETRICS **p_metrics)
+{
+    BOOL    	retval, found;
+    AFMMETRICS	*metrics;
+    INT     	i, len;
+    
+    retval = ReadInt(file, buffer, bufsize, "StartCharMetrics",
+    	    &(afm->NumofMetrics), &found);
+    if (retval == FALSE || found == FALSE)
+    {
+    	*p_metrics = NULL;
+	return retval;
+    }
+    
+    afm->Metrics = *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0,
+    	    afm->NumofMetrics * sizeof(*metrics));
+    if (metrics == NULL)
+    	return FALSE;
+	
+    for (i = 0; i < afm->NumofMetrics; ++i)
+    {
+    	retval = ReadLine(file, buffer, bufsize, &len);
+    	if (retval == FALSE)
+	    goto cleanup_metrics;
+	
+	if(len > 0)
+	{
+	    retval = ParseCharMetrics(buffer, len, metrics + i);
+	    if (retval == FALSE || metrics[i].C == INT_MAX)
+	    	goto cleanup_metrics;
+		
+	    continue;
+	}
+	
+	switch (len)
+	{
+	    case 0: 	    --i;
+		    	    continue;
+				
+	    case INT_MIN:   WARN("Ignoring long line '%32s...'\n", buffer);
+			    goto cleanup_metrics;	    /* retval == TRUE */
+				
+	    case EOF:	    WARN("Unexpected EOF\n");
+	    	    	    goto cleanup_metrics;   	    /* retval == TRUE */
+	}
+    }
+    
+    Unicodify(afm, metrics);	/* wait until all glyph names have been read */
+	    
+    qsort(metrics, afm->NumofMetrics, sizeof(*metrics), AFMMetricsByUV);
+    
+    for (i = 0; metrics[i].UV == -1; ++i);  	/* count unencoded glyphs */
+    
+    if (i != 0)
+    {
+    	AFMMETRICS  *new_metrics;
+    
+    	TRACE("Ignoring %i unencoded glyphs\n", i);
+	
+	afm->NumofMetrics -= i;
+	memmove(metrics, metrics + i, afm->NumofMetrics * sizeof(*metrics));
+	
+	new_metrics = HeapReAlloc(PSDRV_Heap, 0, metrics,
+	    	afm->NumofMetrics * sizeof(*metrics));
+	if (new_metrics == NULL)
+	{
+	    retval = FALSE;
+	    goto cleanup_metrics;
+	}
+	
+	afm->Metrics = *p_metrics = new_metrics;
+    }
+    
+    afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
+    
+    return TRUE;
+    
+    cleanup_metrics:	    	    	/* handle fatal or non-fatal errors */
+    	HeapFree(PSDRV_Heap, 0, metrics);
+	*p_metrics = NULL;
+	return retval;
+}
+
+/*******************************************************************************
+ *  BuildAFM
+ *
+ *  Builds the AFM for a PostScript font and adds it to the driver font list.
+ *  Returns FALSE only on an unexpected error (memory allocation or I/O error).	
+ *
+ */
+static BOOL BuildAFM(FILE *file)
+{
+    CHAR    	buffer[258];    	/* allow for <cr>, <lf>, and <nul> */
+    AFM     	*afm;
+    AFMMETRICS	*metrics;
+    LPSTR   	font_name, full_name, family_name, encoding_scheme;
+    BOOL    	retval, added;
+    
+    retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm);
+    if (retval == FALSE || afm == NULL)
+    	return retval;
+	
+    retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name);
+    if (retval == FALSE || font_name == NULL)
+    	goto cleanup_afm;
+	
+    retval = ReadString(file, buffer, sizeof(buffer), "FullName", &full_name);
+    if (retval == FALSE || full_name == NULL)
+    	goto cleanup_font_name;
+	
+    retval = ReadString(file, buffer, sizeof(buffer), "FamilyName",
+    	    &family_name);
+    if (retval == FALSE || family_name == NULL)
+    	goto cleanup_full_name;
+	
+    retval = ReadString(file, buffer, sizeof(buffer), "EncodingScheme",
+    	    &encoding_scheme);
+    if (retval == FALSE || encoding_scheme == NULL)
+    	goto cleanup_family_name;
+    
+    afm->FontName = font_name;
+    afm->FullName = full_name;
+    afm->FamilyName = family_name;
+    afm->EncodingScheme = encoding_scheme;
+	
+    retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics);
+    if (retval == FALSE || metrics == FALSE)
+    	goto cleanup_encoding_scheme;
+	
+    retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
+    if (retval == FALSE || added == FALSE)
+    	goto cleanup_encoding_scheme;
+	
+    return TRUE;
+    
+    /* clean up after fatal or non-fatal errors */
+    
+    cleanup_encoding_scheme:
+    	HeapFree(PSDRV_Heap, 0, encoding_scheme);
+    cleanup_family_name:
+    	HeapFree(PSDRV_Heap, 0, family_name);
+    cleanup_full_name:
+    	HeapFree(PSDRV_Heap, 0, full_name);
+    cleanup_font_name:
+    	HeapFree(PSDRV_Heap, 0, font_name);
+    cleanup_afm:
+    	HeapFree(PSDRV_Heap, 0, afm);
+	
+    return retval;
+}
+
+/*******************************************************************************
+ *  ReadAFMFile
+ *
+ *  Reads font metrics from Type 1 AFM file.  Only returns FALSE for
+ *  unexpected errors (memory allocation or I/O).
+ *
+ */
+static BOOL ReadAFMFile(LPCSTR filename)
+{
+    FILE    *f;
+    BOOL    retval;
+
+    TRACE("%s\n", filename);
+    
+    f = fopen(filename, "r");
+    if (f == NULL)
+    {
+    	WARN("%s: %s\n", filename, strerror(errno));
+	return TRUE;
+    }
+    
+    retval = BuildAFM(f);
+    
+    fclose(f);
+    return retval;
+}
+
+/*******************************************************************************
+ *  ReadAFMDir
+ *
+ *  Reads all Type 1 AFM files in a directory.
+ *
+ */
+static BOOL ReadAFMDir(LPCSTR dirname)
+{
+    struct dirent   *dent;
+    DIR     	    *dir;
+    CHAR    	    filename[256];
+    
+    dir = opendir(dirname);
+    if (dir == NULL)
+    {
+    	WARN("%s: %s\n", dirname, strerror(errno));
+	return TRUE;
+    }
+    
+    while ((dent = readdir(dir)) != NULL)
+    {
+    	CHAR	*file_extension = strchr(dent->d_name, '.');
+	int 	fn_len;
+	
+	if (file_extension == NULL || strcasecmp(file_extension, ".afm") != 0)
+	    continue;
+	    
+	fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
+	if (fn_len < 0 || fn_len > sizeof(filename) - 1)
+	{
+	    WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
+	    continue;
+	}
+	
+	if (ReadAFMFile(filename) == FALSE)
+	{
+	    closedir(dir);
+	    return FALSE;
+	}
+    }
+    
+    closedir(dir);
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  PSDRV_GetType1Metrics
+ *
+ *  Reads font metrics from Type 1 AFM font files in directories listed in the
+ *  [afmdirs] section of the Wine configuration file.
+ *
+ *  If this function fails (returns FALSE), the dirver will fail to initialize
+ *  and the driver heap will be destroyed, so it's not necessary to HeapFree
+ *  everything in that event.
+ *
+ */
+BOOL PSDRV_GetType1Metrics(void)
+{
+    CHAR    name_buf[256], value_buf[256];
+    INT     i = 0;
+    HKEY    hkey;
+    DWORD   type, name_len, value_len;
+    
+    if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+    	    "Software\\Wine\\Wine\\Config\\afmdirs",
+	    0, KEY_READ, &hkey) != ERROR_SUCCESS)
+	return TRUE;
+	
+    name_len = sizeof(name_buf);
+    value_len = sizeof(value_buf);
+    
+    while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf,
+    	    &value_len) == ERROR_SUCCESS)
+    {
+    	value_buf[sizeof(value_buf) - 1] = '\0';
+	
+	if (ReadAFMDir(value_buf) == FALSE)
+	{
+	    RegCloseKey(hkey);
+	    return FALSE;
+	}
+	
+	/* initialize lengths for new iteration */
+	
+	name_len = sizeof(name_buf);
+	value_len = sizeof(value_buf);
+    }
+    
+    RegCloseKey(hkey);
+    return TRUE;
+}


More information about the wine-patches mailing list