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