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