Removed HEAP_strdupA.
[wine] / dlls / wineps / afm.c
1 /*
2  *      Adobe Font Metric (AFM) file parsing
3  *      See http://partners.adobe.com/asn/developer/PDFS/TN/5004.AFM_Spec.pdf
4  *
5  *      Copyright 1998  Huw D M Davies
6  * 
7  */
8
9 #include "config.h"
10
11 #include <string.h>
12 #include <stdlib.h>     /* qsort() & bsearch() */
13 #include <stdio.h>
14 #include <dirent.h>
15 #include <limits.h>     /* INT_MIN */
16 #ifdef HAVE_FLOAT_H
17 # include <float.h>     /* FLT_MAX */
18 #endif
19 #include "winnt.h"      /* HEAP_ZERO_MEMORY */
20 #include "winreg.h"
21 #include "psdrv.h"
22 #include "debugtools.h"
23
24 DEFAULT_DEBUG_CHANNEL(psdrv);
25 #include <ctype.h>
26
27 /* ptr to fonts for which we have afm files */
28 FONTFAMILY *PSDRV_AFMFontList = NULL;
29
30 /* qsort/bsearch callback functions */
31 typedef int (*compar_callback_fn) (const void *, const void *);
32
33 static VOID SortFontMetrics(AFM *afm, AFMMETRICS *metrics);
34 static VOID CalcWindowsMetrics(AFM *afm);
35 static void PSDRV_ReencodeCharWidths(AFM *afm);
36
37 /*******************************************************************************
38  *  IsWinANSI
39  *
40  *  Checks whether Unicode value is part of Microsoft code page 1252
41  *
42  */
43 static const INT ansiChars[21] =
44 {
45     0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
46     0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
47     0x20ac, 0x2122, 0x2219
48 };
49
50 static int cmpUV(const INT *a, const INT *b)
51 {
52     return *a - *b;
53 }
54  
55 inline static BOOL IsWinANSI(INT uv)
56 {
57     if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) ||
58             (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) ||
59             (0x2020 <= uv && uv <= 2022))
60         return TRUE;
61         
62     if (bsearch(&uv, ansiChars, 21, sizeof(INT),
63             (compar_callback_fn)cmpUV) != NULL)
64         return TRUE;
65         
66     return FALSE;
67 }
68
69 /*******************************************************************************
70  *      CheckMetrics
71  *
72  *  Check an AFMMETRICS structure to make sure all elements have been properly
73  *  filled in.  (Don't check UV or L.)
74  *
75  */
76 static const AFMMETRICS badMetrics =
77 {
78     INT_MIN,                                    /* C */
79     INT_MIN,                                    /* UV */
80     FLT_MAX,                                    /* WX */
81     NULL,                                       /* N */
82     { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX },     /* B */
83     NULL                                        /* L */
84 };
85
86 inline static BOOL CheckMetrics(const AFMMETRICS *metrics)
87 {
88     if (    metrics->C      == badMetrics.C     ||
89             metrics->WX     == badMetrics.WX    ||
90             metrics->N      == badMetrics.N     ||
91             metrics->B.llx  == badMetrics.B.llx ||
92             metrics->B.lly  == badMetrics.B.lly ||
93             metrics->B.urx  == badMetrics.B.urx ||
94             metrics->B.ury  == badMetrics.B.ury )
95         return FALSE;
96         
97     return TRUE;
98 }
99
100
101 /***********************************************************
102  *
103  *      PSDRV_AFMGetCharMetrics
104  *
105  * Parses CharMetric section of AFM file.
106  *
107  * Actually only collects the widths of numbered chars and puts then in
108  * afm->CharWidths.
109  */
110 static BOOL PSDRV_AFMGetCharMetrics(AFM *afm, FILE *fp)
111 {
112     unsigned char line[256], valbuf[256];
113     unsigned char *cp, *item, *value, *curpos, *endpos;
114     int i;
115     AFMMETRICS *metric, *retval;
116
117     metric = HeapAlloc( PSDRV_Heap, 0, afm->NumofMetrics * sizeof(AFMMETRICS));
118     if (metric == NULL)
119         return FALSE;
120         
121     retval = metric;
122         
123     for(i = 0; i < afm->NumofMetrics; i++, metric++) {
124     
125         *metric = badMetrics;
126
127         do {
128             if(!fgets(line, sizeof(line), fp)) {
129                 ERR("Unexpected EOF\n");
130                 HeapFree(PSDRV_Heap, 0, retval);
131                 return FALSE;
132             }
133             cp = line + strlen(line);
134             do {
135                 *cp = '\0';
136                 cp--;
137             } while(cp >= line && isspace(*cp));
138         } while (!(*line));
139
140         curpos = line;
141         while(*curpos) {
142             item = curpos;
143             while(isspace(*item))
144                 item++;
145             value = strpbrk(item, " \t");
146             if (!value) {
147                 ERR("No whitespace found.\n");
148                 HeapFree(PSDRV_Heap, 0, retval);
149                 return FALSE;
150             }
151             while(isspace(*value))
152                 value++;
153             cp = endpos = strchr(value, ';');
154             if (!cp) {
155                 ERR("missing ;, failed. [%s]\n", line);
156                 HeapFree(PSDRV_Heap, 0, retval);
157                 return FALSE;
158             }
159             while(isspace(*--cp))
160                 ;
161             memcpy(valbuf, value, cp - value + 1);
162             valbuf[cp - value + 1] = '\0';
163             value = valbuf;
164
165             if(!strncmp(item, "C ", 2)) {
166                 value = strchr(item, ' ');
167                 sscanf(value, " %d", &metric->C);
168
169             } else if(!strncmp(item, "CH ", 3)) {
170                 value = strrchr(item, ' ');
171                 sscanf(value, " %x", &metric->C);
172             }
173
174             else if(!strncmp("WX ", item, 3) || !strncmp("W0X ", item, 4)) {
175                 sscanf(value, "%f", &metric->WX);
176                 if(metric->C >= 0 && metric->C <= 0xff)
177                     afm->CharWidths[metric->C] = metric->WX;
178             }
179
180             else if(!strncmp("N ", item, 2)) {
181                 metric->N = PSDRV_GlyphName(value);
182             }
183
184             else if(!strncmp("B ", item, 2)) {
185                 sscanf(value, "%f%f%f%f", &metric->B.llx, &metric->B.lly,
186                                           &metric->B.urx, &metric->B.ury);
187
188                 /* Store height of Aring to use as lfHeight */
189                 if(metric->N && !strncmp(metric->N->sz, "Aring", 5))
190                     afm->FullAscender = metric->B.ury;
191             }
192
193             /* Ligatures go here... */
194
195             curpos = endpos + 1;
196         }
197         
198         if (CheckMetrics(metric) == FALSE) {
199             ERR("Error parsing character metrics\n");
200             HeapFree(PSDRV_Heap, 0, retval);
201             return FALSE;
202         }
203
204         TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n",
205               metric->N->sz, metric->WX, metric->B.llx, metric->B.lly,
206               metric->B.urx, metric->B.ury);
207     }
208     
209     SortFontMetrics(afm, retval);
210     
211     afm->Metrics = retval;
212
213     return TRUE;
214 }
215
216
217 /***********************************************************
218  *
219  *      PSDRV_AFMParse
220  *
221  * Fills out an AFM structure and associated substructures (see psdrv.h)
222  * for a given AFM file. All memory is allocated from the driver heap. 
223  * Returns a ptr to the AFM structure or NULL on error.
224  *
225  * This is not complete (we don't handle kerning yet) and not efficient
226  */
227
228 static const AFM *PSDRV_AFMParse(char const *file)
229 {
230     FILE *fp;
231     unsigned char buf[256];
232     unsigned char *value;
233     AFM *afm;
234     unsigned char *cp;
235     int afmfile = 0; 
236     int c;
237     LPSTR font_name = NULL, full_name = NULL, family_name = NULL,
238             encoding_scheme = NULL;
239
240     TRACE("parsing '%s'\n", file);
241
242     if((fp = fopen(file, "r")) == NULL) {
243         MESSAGE("Can't open AFM file '%s'. Please check wine.conf .\n", file);
244         return NULL;
245     }
246
247     afm = HeapAlloc(PSDRV_Heap, 0, sizeof(AFM));
248     if(!afm) {
249         fclose(fp);
250         return NULL;
251     }
252
253     cp = buf; 
254     while ( ( c = fgetc ( fp ) ) != EOF ) {
255         *cp = c;
256         if ( *cp == '\r' || *cp == '\n' || cp - buf == sizeof(buf)-2 ) {
257             if ( cp == buf ) 
258                 continue;
259             *(cp+1)='\0';
260         }
261         else {
262             cp ++; 
263             continue;
264         }
265       
266         cp = buf + strlen(buf);
267         do {
268             *cp = '\0';
269             cp--;
270         } while(cp > buf && isspace(*cp));
271
272         cp = buf; 
273
274         if ( afmfile == 0 && strncmp ( buf, "StartFontMetrics", 16 ) )
275             break;
276         afmfile = 1; 
277
278         value = strchr(buf, ' ');
279         if(value)
280             while(isspace(*value))
281                 value++;
282
283         if(!strncmp("FontName", buf, 8)) {
284             if (!(afm->FontName = font_name = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1 )))
285                 goto cleanup_fp;
286             strcpy( (char *)afm->FontName, value );
287             continue;
288         }
289
290         if(!strncmp("FullName", buf, 8)) {
291             if (!(afm->FullName = full_name = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1 )))
292                 goto cleanup_fp;
293             strcpy( (char *)afm->FullName, value );
294             continue;
295         }
296
297         if(!strncmp("FamilyName", buf, 10)) {
298             if (!(afm->FamilyName = family_name = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1 )))
299                 goto cleanup_fp;
300             strcpy( (char *)afm->FamilyName, value );
301             continue;
302         }
303         
304         if(!strncmp("Weight", buf, 6)) {
305             if(!strncmp("Roman", value, 5) || !strncmp("Medium", value, 6)
306                || !strncmp("Book", value, 4) || !strncmp("Regular", value, 7)
307                || !strncmp("Normal", value, 6))
308                 afm->Weight = FW_NORMAL;
309             else if(!strncmp("Demi", value, 4))
310                 afm->Weight = FW_DEMIBOLD;
311             else if(!strncmp("Bold", value, 4))
312                 afm->Weight = FW_BOLD;
313             else if(!strncmp("Light", value, 5))
314                 afm->Weight = FW_LIGHT;
315             else if(!strncmp("Black", value, 5))
316                 afm->Weight = FW_BLACK;
317             else {
318                 WARN("%s specifies unknown Weight '%s'; treating as Roman\n",
319                      file, value);
320                 afm->Weight = FW_NORMAL;
321             }
322             continue;
323         }
324
325         if(!strncmp("ItalicAngle", buf, 11)) {
326             sscanf(value, "%f", &(afm->ItalicAngle));
327             continue;
328         }
329
330         if(!strncmp("IsFixedPitch", buf, 12)) {
331             if(!strncasecmp("false", value, 5))
332                 afm->IsFixedPitch = FALSE;
333             else
334                 afm->IsFixedPitch = TRUE;
335             continue;
336         }
337
338         if(!strncmp("FontBBox", buf, 8)) {
339             sscanf(value, "%f %f %f %f", &(afm->FontBBox.llx), 
340                    &(afm->FontBBox.lly), &(afm->FontBBox.urx), 
341                    &(afm->FontBBox.ury) );
342             continue;
343         }
344
345         if(!strncmp("UnderlinePosition", buf, 17)) {
346             sscanf(value, "%f", &(afm->UnderlinePosition) );
347             continue;
348         }
349
350         if(!strncmp("UnderlineThickness", buf, 18)) {
351             sscanf(value, "%f", &(afm->UnderlineThickness) );
352             continue;
353         }
354
355         if(!strncmp("CapHeight", buf, 9)) {
356             sscanf(value, "%f", &(afm->CapHeight) );
357             continue;
358         }
359
360         if(!strncmp("XHeight", buf, 7)) {
361             sscanf(value, "%f", &(afm->XHeight) );
362             continue;
363         }
364
365         if(!strncmp("Ascender", buf, 8)) {
366             sscanf(value, "%f", &(afm->Ascender) );
367             continue;
368         }
369
370         if(!strncmp("Descender", buf, 9)) {
371             sscanf(value, "%f", &(afm->Descender) );
372             continue;
373         }
374
375         if(!strncmp("StartCharMetrics", buf, 16)) {
376             sscanf(value, "%d", &(afm->NumofMetrics) );
377             if (PSDRV_AFMGetCharMetrics(afm, fp) == FALSE)
378                 goto cleanup_fp;
379             continue;
380         }
381
382         if(!strncmp("EncodingScheme", buf, 14)) {
383             if (!(afm->EncodingScheme = encoding_scheme = HeapAlloc(PSDRV_Heap, 0, strlen(value)+1)))
384                 goto cleanup_fp;
385             strcpy( (char *)afm->EncodingScheme, value );
386             continue;
387         }
388
389     }
390     fclose(fp);
391
392     if (afmfile == 0) {
393         HeapFree ( PSDRV_Heap, 0, afm ); 
394         return NULL;
395     }
396
397     if(afm->FontName == NULL) {
398         WARN("%s contains no FontName.\n", file);
399         if (!(afm->FontName = font_name = HeapAlloc(PSDRV_Heap, 0, 7)))
400             goto cleanup;
401         strcpy( (char *)afm->FontName, "nofont" );
402     }
403     if(afm->FullName == NULL)
404     {
405         if (!(afm->FullName = full_name = HeapAlloc(PSDRV_Heap, 0, strlen(afm->FontName)+1 )))
406             goto cleanup;
407         strcpy( (char *)afm->FullName, afm->FontName );
408     }
409     if(afm->FamilyName == NULL)
410     {
411         if (!(afm->FamilyName = family_name = HeapAlloc(PSDRV_Heap, 0, strlen(afm->FontName)+1 )))
412             goto cleanup;
413         strcpy( (char *)afm->FamilyName, afm->FontName );
414     }
415
416     if(afm->Ascender == 0.0)
417         afm->Ascender = afm->FontBBox.ury;
418     if(afm->Descender == 0.0)
419         afm->Descender = afm->FontBBox.lly;
420     if(afm->FullAscender == 0.0)
421         afm->FullAscender = afm->Ascender;
422     if(afm->Weight == 0)
423         afm->Weight = FW_NORMAL;
424         
425     CalcWindowsMetrics(afm);
426     
427     if (afm->EncodingScheme != NULL &&
428             strcmp(afm->EncodingScheme, "AdobeStandardEncoding") == 0)
429         PSDRV_ReencodeCharWidths(afm);
430         
431     return afm;
432     
433     cleanup_fp:
434     
435         fclose(fp);
436     
437     cleanup:
438     
439         if (font_name == NULL)
440             HeapFree(PSDRV_Heap, 0, font_name);
441         if (full_name == NULL)
442             HeapFree(PSDRV_Heap, 0, full_name);
443         if (family_name == NULL)
444             HeapFree(PSDRV_Heap, 0, family_name);
445         if (encoding_scheme == NULL)
446             HeapFree(PSDRV_Heap, 0, encoding_scheme);
447             
448         HeapFree(PSDRV_Heap, 0, afm);
449             
450         return NULL;
451 }
452
453 /***********************************************************
454  *
455  *      PSDRV_FreeAFMList
456  *
457  * Frees the family and afmlistentry structures in list head
458  */
459 void PSDRV_FreeAFMList( FONTFAMILY *head )
460 {
461     AFMLISTENTRY *afmle, *nexta;
462     FONTFAMILY *family, *nextf;
463
464     for(nextf = family = head; nextf; family = nextf) {
465         for(nexta = afmle = family->afmlist; nexta; afmle = nexta) {
466             nexta = afmle->next;
467             HeapFree( PSDRV_Heap, 0, afmle );
468         }
469         nextf = family->next;
470         HeapFree( PSDRV_Heap, 0, family );
471     }
472     return;
473 }
474
475
476 /***********************************************************
477  *
478  *      PSDRV_FindAFMinList
479  * Returns ptr to an AFM if name (which is a PS font name) exists in list
480  * headed by head.
481  */
482 const AFM *PSDRV_FindAFMinList(FONTFAMILY *head, char *name)
483 {
484     FONTFAMILY *family;
485     AFMLISTENTRY *afmle;
486
487     for(family = head; family; family = family->next) {
488         for(afmle = family->afmlist; afmle; afmle = afmle->next) {
489             if(!strcmp(afmle->afm->FontName, name))
490                 return afmle->afm;
491         }
492     }
493     return NULL;
494 }
495
496 /***********************************************************
497  *
498  *      PSDRV_AddAFMtoList
499  *
500  * Adds an afm to the list whose head is pointed to by head. Creates new
501  * family node if necessary and always creates a new AFMLISTENTRY.
502  */
503 BOOL PSDRV_AddAFMtoList(FONTFAMILY **head, const AFM *afm)
504 {
505     FONTFAMILY *family = *head;
506     FONTFAMILY **insert = head;
507     AFMLISTENTRY *tmpafmle, *newafmle;
508
509     newafmle = HeapAlloc(PSDRV_Heap, HEAP_ZERO_MEMORY,
510                            sizeof(*newafmle));
511     if (newafmle == NULL)
512         return FALSE;
513         
514     newafmle->afm = afm;
515
516     while(family) {
517         if(!strcmp(family->FamilyName, afm->FamilyName))
518             break;
519         insert = &(family->next);
520         family = family->next;
521     }
522  
523     if(!family) {
524         family = HeapAlloc(PSDRV_Heap, HEAP_ZERO_MEMORY,
525                            sizeof(*family));
526         if (family == NULL) {
527             HeapFree(PSDRV_Heap, 0, newafmle);
528             return FALSE;
529         }
530         *insert = family;
531         if (!(family->FamilyName = HeapAlloc(PSDRV_Heap, 0, strlen(afm->FamilyName)+1 ))) {
532             HeapFree(PSDRV_Heap, 0, family);
533             HeapFree(PSDRV_Heap, 0, newafmle);
534             return FALSE;
535         }
536         strcpy( family->FamilyName, afm->FamilyName );
537         family->afmlist = newafmle;
538         return TRUE;
539     }
540     else {
541         tmpafmle = family->afmlist;
542         while (tmpafmle) {
543             if (!strcmp(tmpafmle->afm->FontName, afm->FontName)) {
544                 WARN("Ignoring duplicate FontName '%s'\n", afm->FontName);
545                 HeapFree(PSDRV_Heap, 0, newafmle);
546                 return TRUE;                        /* not a fatal error */
547             }
548             tmpafmle = tmpafmle->next;
549         }
550     }
551     
552     tmpafmle = family->afmlist;
553     while(tmpafmle->next)
554         tmpafmle = tmpafmle->next;
555
556     tmpafmle->next = newafmle;
557
558     return TRUE;
559 }
560
561 /**********************************************************
562  *
563  *      PSDRV_ReencodeCharWidths
564  *
565  * Re map the CharWidths field of the afm to correspond to an ANSI encoding
566  *
567  */
568 static void PSDRV_ReencodeCharWidths(AFM *afm)
569 {
570     int i, j;
571     const AFMMETRICS *metric;
572
573     for(i = 0; i < 256; i++) {
574         if(isalnum(i))
575             continue;
576         if(PSDRV_ANSIVector[i] == NULL) {
577             afm->CharWidths[i] = 0.0;
578             continue;
579         }
580         for (j = 0, metric = afm->Metrics; j < afm->NumofMetrics; j++, metric++) {
581             if(metric->N && !strcmp(metric->N->sz, PSDRV_ANSIVector[i])) {
582                 afm->CharWidths[i] = metric->WX;
583                 break;
584             }
585         }
586         if(j == afm->NumofMetrics) {
587             WARN("Couldn't find glyph '%s' in font '%s'\n",
588                  PSDRV_ANSIVector[i], afm->FontName);
589             afm->CharWidths[i] = 0.0;
590         }
591     }
592     return;
593 }
594
595
596 /***********************************************************
597  *
598  *      PSDRV_DumpFontList
599  *
600  */
601 static void PSDRV_DumpFontList(void)
602 {
603     FONTFAMILY      *family;
604     AFMLISTENTRY    *afmle;
605
606     for(family = PSDRV_AFMFontList; family; family = family->next) {
607         TRACE("Family '%s'\n", family->FamilyName);
608         for(afmle = family->afmlist; afmle; afmle = afmle->next)
609         {
610             INT i;
611             
612             TRACE("\tFontName '%s' (%i glyphs) - '%s' encoding:\n",
613                     afmle->afm->FontName, afmle->afm->NumofMetrics,
614                     afmle->afm->EncodingScheme);
615             
616             for (i = 0; i < afmle->afm->NumofMetrics; ++i)
617             {
618                 TRACE("\t\tU+%.4lX; C %i; N '%s'\n", afmle->afm->Metrics[i].UV,
619                         afmle->afm->Metrics[i].C, afmle->afm->Metrics[i].N->sz);
620             }
621         }
622     }
623     return;
624 }
625
626 /*******************************************************************************
627  *  SortFontMetrics
628  *
629  *  Initializes the UV member of each glyph's AFMMETRICS and sorts each font's
630  *  Metrics by Unicode Value.  If the font has a standard encoding (i.e. it is
631  *  using the Adobe Glyph List encoding vector), look up each glyph's Unicode
632  *  Value based on it's glyph name.  If the font has a font-specific encoding,
633  *  map the default PostScript encodings into the Unicode private use area.
634  *
635  */
636 static int UnicodeGlyphByNameIndex(const UNICODEGLYPH *a, const UNICODEGLYPH *b)
637 {
638     return a->name->index - b->name->index;
639 }
640
641 static int AFMMetricsByUV(const AFMMETRICS *a, const AFMMETRICS *b)
642 {
643     return a->UV - b->UV;
644 }
645  
646 static VOID SortFontMetrics(AFM *afm, AFMMETRICS *metrics)
647 {
648     INT     i;
649     
650     TRACE("%s\n", afm->FontName);
651     
652     if (strcmp(afm->EncodingScheme, "FontSpecific") != 0)
653     {
654         PSDRV_IndexGlyphList();         /* enable searching by name index */
655             
656         for (i = 0; i < afm->NumofMetrics; ++i)
657         {
658             UNICODEGLYPH    ug, *pug;
659                     
660             ug.name = metrics[i].N;
661                     
662             pug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
663                     sizeof(UNICODEGLYPH),
664                     (compar_callback_fn)UnicodeGlyphByNameIndex);
665             if (pug == NULL)
666             {
667                 WARN("Glyph '%s' in font '%s' does not have a UV\n",
668                         ug.name->sz, afm->FullName);
669                 metrics[i].UV = -1;
670             }
671             else
672             {
673                 metrics[i].UV = pug->UV;
674             }
675         }
676     }
677     else                                    /* FontSpecific encoding */
678     {
679         for (i = 0; i < afm->NumofMetrics; ++i)
680             metrics[i].UV = metrics[i].C;
681     }
682             
683     qsort(metrics, afm->NumofMetrics, sizeof(AFMMETRICS),
684             (compar_callback_fn)AFMMetricsByUV);
685                     
686     for (i = 0; i < afm->NumofMetrics; ++i)     /* count unencoded glyphs */
687         if (metrics[i].UV >= 0)
688             break;
689             
690     if (i != 0)
691     {
692         TRACE("Ignoring %i unencoded glyphs\n", i);
693         afm->NumofMetrics -= i;
694         memmove(metrics, metrics + i, afm->NumofMetrics * sizeof(*metrics));
695     }
696 }
697
698 /*******************************************************************************
699  *  PSDRV_CalcAvgCharWidth
700  *
701  *  Calculate WinMetrics.sAvgCharWidth for a Type 1 font.  Can also be used on
702  *  TrueType fonts, if font designer set OS/2:xAvgCharWidth to zero.
703  *
704  *  Tries to use formula in TrueType specification; falls back to simple mean
705  *  if any lowercase latin letter (or space) is not present.
706  */
707 inline static SHORT MeanCharWidth(const AFM *afm)
708 {
709     float   w = 0.0;
710     int     i;
711     
712     for (i = 0; i < afm->NumofMetrics; ++i)
713         w += afm->Metrics[i].WX;
714         
715     w /= afm->NumofMetrics;
716     
717     return (SHORT)(w + 0.5);
718 }
719
720 static const struct { LONG UV; int weight; } UVweight[27] =
721 {
722     { 0x0061,  64 }, { 0x0062,  14 }, { 0x0063,  27 }, { 0x0064,  35 },
723     { 0x0065, 100 }, { 0x0066,  20 }, { 0x0067,  14 }, { 0x0068,  42 },
724     { 0x0069,  63 }, { 0x006a,   3 }, { 0x006b,   6 }, { 0x006c,  35 },
725     { 0x006d,  20 }, { 0x006e,  56 }, { 0x006f,  56 }, { 0x0070,  17 },
726     { 0x0071,   4 }, { 0x0072,  49 }, { 0x0073,  56 }, { 0x0074,  71 },
727     { 0x0075,  31 }, { 0x0076,  10 }, { 0x0077,  18 }, { 0x0078,   3 },
728     { 0x0079,  18 }, { 0x007a,   2 }, { 0x0020, 166 }
729 };
730  
731 SHORT PSDRV_CalcAvgCharWidth(const AFM *afm)
732 {
733     float   w = 0.0;
734     int     i;
735     
736     for (i = 0; i < 27; ++i)
737     {
738         const AFMMETRICS    *afmm;
739         
740         afmm = PSDRV_UVMetrics(UVweight[i].UV, afm);
741         if (afmm->UV != UVweight[i].UV)     /* UVMetrics returns first glyph */
742             return MeanCharWidth(afm);      /*   in font if UV is missing    */
743             
744         w += afmm->WX * (float)(UVweight[i].weight);
745     }
746     
747     w /= 1000.0;
748     
749     return (SHORT)(w + 0.5);
750 }
751
752 /*******************************************************************************
753  *  CalcWindowsMetrics
754  *
755  *  Calculates several Windows-specific font metrics for each font.
756  *
757  */
758 static VOID CalcWindowsMetrics(AFM *afm)
759 {
760     WINMETRICS  wm;
761     INT         i;
762             
763     wm.usUnitsPerEm = 1000;                     /* for PostScript fonts */
764     wm.sTypoAscender = (SHORT)(afm->Ascender + 0.5);
765     wm.sTypoDescender = (SHORT)(afm->Descender - 0.5);
766             
767     wm.sTypoLineGap = 1200 - (wm.sTypoAscender - wm.sTypoDescender);
768     if (wm.sTypoLineGap < 0)
769         wm.sTypoLineGap = 0;
770                 
771     wm.usWinAscent = 0;
772     wm.usWinDescent = 0;
773             
774     for (i = 0; i < afm->NumofMetrics; ++i)
775     {
776         if (IsWinANSI(afm->Metrics[i].UV) == FALSE)
777             continue;
778             
779         if (afm->Metrics[i].B.ury > 0)
780         {
781             USHORT ascent = (USHORT)(afm->Metrics[i].B.ury + 0.5);
782                                         
783             if (ascent > wm.usWinAscent)
784                 wm.usWinAscent = ascent;
785         }
786         
787         if (afm->Metrics[i].B.lly < 0)    
788         {
789             USHORT descent = (USHORT)(-(afm->Metrics[i].B.lly) + 0.5);
790             
791             if (descent > wm.usWinDescent)
792                 wm.usWinDescent = descent;
793         }
794     }
795     
796     if (wm.usWinAscent == 0 && afm->FontBBox.ury > 0)
797         wm.usWinAscent = (USHORT)(afm->FontBBox.ury + 0.5);
798         
799     if (wm.usWinDescent == 0 && afm->FontBBox.lly < 0)
800         wm.usWinDescent = (USHORT)(-(afm->FontBBox.lly) + 0.5);
801                 
802     wm.sAscender = wm.usWinAscent;
803     wm.sDescender = -(wm.usWinDescent);
804             
805     wm.sLineGap = 1150 - (wm.sAscender - wm.sDescender);
806     if (wm.sLineGap < 0)
807         wm.sLineGap = 0;
808                 
809     wm.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
810                                                 
811     TRACE("Windows metrics for '%s':\n", afm->FullName);
812     TRACE("\tsAscender = %i\n", wm.sAscender);
813     TRACE("\tsDescender = %i\n", wm.sDescender);
814     TRACE("\tsLineGap = %i\n", wm.sLineGap);
815     TRACE("\tusUnitsPerEm = %u\n", wm.usUnitsPerEm);
816     TRACE("\tsTypoAscender = %i\n", wm.sTypoAscender);
817     TRACE("\tsTypoDescender = %i\n", wm.sTypoDescender);
818     TRACE("\tsTypoLineGap = %i\n", wm.sTypoLineGap);
819     TRACE("\tusWinAscent = %u\n", wm.usWinAscent);
820     TRACE("\tusWinDescent = %u\n", wm.usWinDescent);
821     TRACE("\tsAvgCharWidth = %i\n", wm.sAvgCharWidth);
822             
823     afm->WinMetrics = wm;
824             
825     /* See afm2c.c and mkagl.c for an explanation of this */
826     /*  PSDRV_AFM2C(afm);   */
827 }
828
829
830 /*******************************************************************************
831  *  AddBuiltinAFMs
832  *
833  */
834  
835 static BOOL AddBuiltinAFMs()
836 {
837     int i = 0;
838     
839     while (PSDRV_BuiltinAFMs[i] != NULL)
840     {
841         if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, PSDRV_BuiltinAFMs[i])
842                 == FALSE)
843             return FALSE;
844         ++i;
845     }
846     
847     return TRUE;
848 }
849
850
851 /***********************************************************
852  *
853  *      PSDRV_GetFontMetrics
854  *
855  * Parses all afm files listed in [afmfiles] and [afmdirs] of wine.conf
856  *
857  * If this function fails, PSDRV_Init will destroy PSDRV_Heap, so don't worry
858  * about freeing all the memory that's been allocated.
859  */
860
861 static BOOL PSDRV_ReadAFMDir(const char* afmdir) {
862     DIR *dir;
863     const AFM   *afm;
864
865     dir = opendir(afmdir);
866     if (dir) {
867         struct dirent *dent;
868         while ((dent=readdir(dir))) {
869             if (strstr(dent->d_name,".afm")) {
870                 char *afmfn;
871
872                 afmfn=(char*)HeapAlloc(PSDRV_Heap,0, 
873                         strlen(afmdir)+strlen(dent->d_name)+2);
874                 if (afmfn == NULL) {
875                     closedir(dir);
876                     return FALSE;
877                 }
878                 strcpy(afmfn,afmdir);
879                 strcat(afmfn,"/");
880                 strcat(afmfn,dent->d_name);
881                 TRACE("loading AFM %s\n",afmfn);
882                 afm = PSDRV_AFMParse(afmfn);
883                 if (afm) {
884                     if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm) == FALSE) {
885                         closedir(dir);
886                         return FALSE;
887                     }
888                 }
889                 else {
890                     WARN("Error parsing %s\n", afmfn);
891                 }
892                 HeapFree(PSDRV_Heap,0,afmfn);
893             }
894         }
895         closedir(dir);
896     }
897     else {
898         WARN("Error opening %s\n", afmdir);
899     }
900     
901     return TRUE;
902 }
903
904 BOOL PSDRV_GetFontMetrics(void)
905 {
906     int idx;
907     char key[256];
908     char value[256];
909     HKEY hkey;
910     DWORD type, key_len, value_len;
911
912     if (PSDRV_GlyphListInit() != 0)
913         return FALSE;
914
915     if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\afmfiles",
916                      0, KEY_READ, &hkey))
917         goto no_afmfiles;
918
919     idx = 0;
920     key_len = sizeof(key);
921     value_len = sizeof(value);
922     while(!RegEnumValueA(hkey, idx++, key, &key_len, NULL, &type, value, &value_len))
923     {
924         const AFM* afm = PSDRV_AFMParse(value);
925         
926         if (afm) {
927             if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm) == FALSE) {
928                 RegCloseKey(hkey);
929                 return FALSE;
930             }
931         }
932         else {
933             WARN("Error parsing %s\n", value);
934         }
935
936         /* initialize lengths for new iteration */
937         key_len = sizeof(key);
938         value_len = sizeof(value);
939     }
940     RegCloseKey(hkey);
941
942 no_afmfiles:
943
944     if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\afmdirs",
945                      0, KEY_READ, &hkey))
946         goto no_afmdirs;
947
948     idx = 0;
949     key_len = sizeof(key);
950     value_len = sizeof(value);
951     while(!RegEnumValueA(hkey, idx++, key, &key_len, NULL, &type, value, &value_len))
952     {
953         if (PSDRV_ReadAFMDir (value) == FALSE)
954         {
955             RegCloseKey(hkey);
956             return FALSE;
957         }
958
959         /* initialize lengths for new iteration */
960         key_len = sizeof(key);
961         value_len = sizeof(value);
962     }
963     RegCloseKey(hkey);
964
965 no_afmdirs:
966
967     if (AddBuiltinAFMs() == FALSE)
968         return FALSE;
969
970 #ifdef HAVE_FREETYPE   
971     if (PSDRV_GetTrueTypeMetrics() == FALSE)
972         return FALSE;
973     PSDRV_IndexGlyphList();
974 #endif
975
976     PSDRV_DumpFontList();
977     return TRUE;
978 }