Added an unknown VxD error code.
[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     
597     tmpafmle = family->afmlist;
598     while(tmpafmle->next)
599         tmpafmle = tmpafmle->next;
600
601     tmpafmle->next = newafmle;
602
603     return TRUE;
604 }
605
606 /**********************************************************
607  *
608  *      PSDRV_ReencodeCharWidths
609  *
610  * Re map the CharWidths field of the afm to correspond to an ANSI encoding
611  *
612  */
613 static void PSDRV_ReencodeCharWidths(AFM *afm)
614 {
615     int i, j;
616     AFMMETRICS *metric;
617
618     for(i = 0; i < 256; i++) {
619         if(isalnum(i))
620             continue;
621         if(PSDRV_ANSIVector[i] == NULL) {
622             afm->CharWidths[i] = 0.0;
623             continue;
624         }
625         for (j = 0, metric = afm->Metrics; j < afm->NumofMetrics; j++, metric++) {
626             if(metric->N && !strcmp(metric->N->sz, PSDRV_ANSIVector[i])) {
627                 afm->CharWidths[i] = metric->WX;
628                 break;
629             }
630         }
631         if(j == afm->NumofMetrics) {
632             WARN("Couldn't find glyph '%s' in font '%s'\n",
633                  PSDRV_ANSIVector[i], afm->FontName);
634             afm->CharWidths[i] = 0.0;
635         }
636     }
637     return;
638 }
639
640
641 /***********************************************************
642  *
643  *      PSDRV_DumpFontList
644  *
645  */
646 static void PSDRV_DumpFontList(void)
647 {
648     FONTFAMILY      *family;
649     AFMLISTENTRY    *afmle;
650
651     for(family = PSDRV_AFMFontList; family; family = family->next) {
652         TRACE("Family '%s'\n", family->FamilyName);
653         for(afmle = family->afmlist; afmle; afmle = afmle->next)
654         {
655             INT i;
656             
657             TRACE("\tFontName '%s' (%i glyphs) - '%s' encoding:\n",
658                     afmle->afm->FontName, afmle->afm->NumofMetrics,
659                     afmle->afm->EncodingScheme);
660             
661             for (i = 0; i < afmle->afm->NumofMetrics; ++i)
662             {
663                 TRACE("\t\tU+%.4lX; C %i; N '%s'\n", afmle->afm->Metrics[i].UV,
664                         afmle->afm->Metrics[i].C, afmle->afm->Metrics[i].N->sz);
665             }
666         }
667     }
668     return;
669 }
670
671 /*******************************************************************************
672  *  SortFontMetrics
673  *
674  *  Initializes the UV member of each glyph's AFMMETRICS and sorts each font's
675  *  Metrics by Unicode Value.  If the font has a standard encoding (i.e. it is
676  *  using the Adobe Glyph List encoding vector), look up each glyph's Unicode
677  *  Value based on it's glyph name.  If the font has a font-specific encoding,
678  *  map the default PostScript encodings into the Unicode private use area.
679  *
680  */
681 static int UnicodeGlyphByNameIndex(const UNICODEGLYPH *a, const UNICODEGLYPH *b)
682 {
683     return a->name->index - b->name->index;
684 }
685
686 static int UnicodeGlyphByUV(const UNICODEGLYPH *a, const UNICODEGLYPH *b)
687 {
688     return a->UV - b->UV;
689 }
690  
691 static int AFMMetricsByUV(const AFMMETRICS *a, const AFMMETRICS *b)
692 {
693     return a->UV - b->UV;
694 }
695  
696 static BOOL SortFontMetrics()
697 {
698     UNICODEGLYPH    *aglCopy = NULL;
699     FONTFAMILY      *family = PSDRV_AFMFontList;
700     
701     while (family != NULL)
702     {
703         AFMLISTENTRY    *afmle = family->afmlist;
704         
705         while (afmle != NULL)
706         {
707             AFM *afm = afmle->afm;      /* should always be valid */
708             INT i;
709             
710             if (afm->Encoding == &PSDRV_AdobeGlyphList)
711             {
712                 if (aglCopy == NULL)    /* do this once, if necessary */
713                 {
714                     aglCopy = HeapAlloc(PSDRV_Heap, 0,
715                             PSDRV_AdobeGlyphList.size * sizeof(UNICODEGLYPH));
716                     if (aglCopy == NULL)
717                         return FALSE;
718                         
719                     memcpy(aglCopy, PSDRV_AdobeGlyphList.glyphs,
720                             PSDRV_AdobeGlyphList.size * sizeof(UNICODEGLYPH));
721                             
722                     qsort(aglCopy, PSDRV_AdobeGlyphList.size,
723                             sizeof(UNICODEGLYPH),
724                             (compar_callback_fn)UnicodeGlyphByNameIndex);
725                 }
726                 
727                 for (i = 0; i < afm->NumofMetrics; ++i)
728                 {
729                     UNICODEGLYPH    ug, *pug;
730                     
731                     ug.name = afm->Metrics[i].N;
732                     ug.UV = -1;
733                     
734                     pug = bsearch(&ug, aglCopy, PSDRV_AdobeGlyphList.size,
735                             sizeof(UNICODEGLYPH),
736                             (compar_callback_fn)UnicodeGlyphByNameIndex);
737                     if (pug == NULL)
738                     {
739                         WARN("Glyph '%s' in font '%s' does not have a UV\n",
740                                 ug.name->sz, afm->FullName);
741                         afm->Metrics[i].UV = -1;
742                     }
743                     else
744                     {
745                         afm->Metrics[i].UV = pug->UV;
746                     }
747                 }
748             }
749             else                /* FontSpecific encoding or TrueType font */
750             {
751                 for (i = 0; i < afm->NumofMetrics; ++i)
752                     afm->Metrics[i].UV = afm->Encoding->glyphs[i].UV;
753                 
754                 /* typecast avoids compiler warning */
755                 qsort((void *)(afm->Encoding->glyphs), afm->Encoding->size,
756                         sizeof(UNICODEGLYPH),
757                         (compar_callback_fn)UnicodeGlyphByUV);
758                         
759                 for (i = 0; i < afm->Encoding->size; ++i)
760                     if (afm->Encoding->glyphs[i].UV >= 0)
761                         break;
762                         
763                 afm->Encoding->size -= i;       /* Ignore unencoded glyphs */
764                 afm->Encoding->glyphs += i;     /* from now on */
765             }
766             
767             qsort(afm->Metrics, afm->NumofMetrics, sizeof(AFMMETRICS),
768                     (compar_callback_fn)AFMMetricsByUV);
769                     
770             for (i = 0; i < afm->NumofMetrics; ++i)
771                 if (afm->Metrics[i].UV >= 0)
772                     break;
773                     
774             afm->NumofMetrics -= i;     /* Ignore unencoded glyphs here too */
775             afm->Metrics += i;
776             
777             afmle = afmle->next;
778         }
779         
780         family = family->next;
781     }
782     
783     if (aglCopy != NULL)
784         HeapFree(PSDRV_Heap, 0, aglCopy);
785         
786     return TRUE;
787 }
788
789 /*******************************************************************************
790  *  CalcWindowsMetrics
791  *
792  *  Calculates several Windows-specific font metrics for each font.  Relies on
793  *  the fact that AFMs are allocated with HEAP_ZERO_MEMORY to distinguish
794  *  TrueType fonts (when implemented), which already have these filled in.
795  *
796  */
797 static VOID CalcWindowsMetrics()
798 {
799     FONTFAMILY  *family = PSDRV_AFMFontList;
800     
801     while (family != NULL)
802     {
803         AFMLISTENTRY    *afmle = family->afmlist;
804         
805         while (afmle != NULL)
806         {
807             WINMETRICS  wm;
808             AFM         *afm = afmle->afm;      /* should always be valid */
809             INT         i;
810             
811             if (afm->WinMetrics.usUnitsPerEm != 0)
812                 continue;                           /* TrueType font */
813                 
814             wm.usUnitsPerEm = 1000;                 /* for PostScript fonts */
815             wm.sTypoAscender = (SHORT)(afm->Ascender + 0.5);
816             wm.sTypoDescender = (SHORT)(afm->Descender + 0.5);
817             
818             wm.sTypoLineGap = 1200 - (wm.sTypoAscender - wm.sTypoDescender);
819             if (wm.sTypoLineGap < 0)
820                 wm.sTypoLineGap = 0;
821                 
822             wm.usWinAscent = 0;
823             wm.usWinDescent = 0;
824             
825             for (i = 0; i < afm->NumofMetrics; ++i)
826             {
827                 if (IsWinANSI(afm->Metrics[i].UV) == FALSE)
828                     continue;
829                     
830                 if (afm->Metrics[i].B.ury > 0)
831                 {
832                     USHORT ascent = (USHORT)(afm->Metrics[i].B.ury + 0.5);
833                                                 
834                     if (ascent > wm.usWinAscent)
835                         wm.usWinAscent = ascent;
836                 }
837                 
838                 if (afm->Metrics[i].B.lly < 0)    
839                 {
840                     USHORT descent = (USHORT)(-(afm->Metrics[i].B.lly) + 0.5);
841                     
842                     if (descent > wm.usWinDescent)
843                         wm.usWinDescent = descent;
844                 }
845             }
846             
847             if (wm.usWinAscent == 0 && afm->FontBBox.ury > 0)
848                 wm.usWinAscent = (USHORT)(afm->FontBBox.ury + 0.5);
849                 
850             if (wm.usWinDescent == 0 && afm->FontBBox.lly < 0)
851                 wm.usWinDescent = (USHORT)(-(afm->FontBBox.lly) + 0.5);
852                 
853             wm.sAscender = wm.usWinAscent;
854             wm.sDescender = -(wm.usWinDescent);
855             
856             wm.sLineGap = 1150 - (wm.sAscender - wm.sDescender);
857             if (wm.sLineGap < 0)
858                 wm.sLineGap = 0;
859                                                 
860             TRACE("Windows metrics for '%s':\n", afm->FullName);
861             TRACE("\tsAscender = %i\n", wm.sAscender);
862             TRACE("\tsDescender = %i\n", wm.sDescender);
863             TRACE("\tsLineGap = %i\n", wm.sLineGap);
864             TRACE("\tusUnitsPerEm = %u\n", wm.usUnitsPerEm);
865             TRACE("\tsTypoAscender = %i\n", wm.sTypoAscender);
866             TRACE("\tsTypoDescender = %i\n", wm.sTypoDescender);
867             TRACE("\tsTypoLineGap = %i\n", wm.sTypoLineGap);
868             TRACE("\tusWinAscent = %u\n", wm.usWinAscent);
869             TRACE("\tusWinDescent = %u\n", wm.usWinDescent);
870             
871             afm->WinMetrics = wm;
872             
873             afmle = afmle->next;
874         }
875         
876         family = family ->next;
877     }
878 }
879
880
881 /***********************************************************
882  *
883  *      PSDRV_GetFontMetrics
884  *
885  * Parses all afm files listed in [afmfiles] and [afmdirs] of wine.conf
886  *
887  * If this function fails, PSDRV_Init will destroy PSDRV_Heap, so don't worry
888  * about freeing all the memory that's been allocated.
889  */
890
891 static BOOL PSDRV_ReadAFMDir(const char* afmdir) {
892     DIR *dir;
893     AFM *afm;
894
895     dir = opendir(afmdir);
896     if (dir) {
897         struct dirent *dent;
898         while ((dent=readdir(dir))) {
899             if (strstr(dent->d_name,".afm")) {
900                 char *afmfn;
901
902                 afmfn=(char*)HeapAlloc(PSDRV_Heap,0, 
903                         strlen(afmdir)+strlen(dent->d_name)+2);
904                 if (afmfn == NULL) {
905                     closedir(dir);
906                     return FALSE;
907                 }
908                 strcpy(afmfn,afmdir);
909                 strcat(afmfn,"/");
910                 strcat(afmfn,dent->d_name);
911                 TRACE("loading AFM %s\n",afmfn);
912                 afm = PSDRV_AFMParse(afmfn);
913                 if (afm) {
914                     if(afm->EncodingScheme && 
915                        !strcmp(afm->EncodingScheme,"AdobeStandardEncoding")) {
916                         PSDRV_ReencodeCharWidths(afm);
917                     }
918                     if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm) == FALSE) {
919                         closedir(dir);
920                         FreeAFM(afm);
921                         return FALSE;
922                     }
923                 }
924                 else {
925                     WARN("Error parsing %s\n", afmfn);
926                 }
927                 HeapFree(PSDRV_Heap,0,afmfn);
928             }
929         }
930         closedir(dir);
931     }
932     else {
933         WARN("Error opening %s\n", afmdir);
934     }
935     
936     return TRUE;
937 }
938
939 BOOL PSDRV_GetFontMetrics(void)
940 {
941     int idx = 0;
942     char key[256];
943     char value[256];
944
945     if (PSDRV_GlyphListInit() != 0)
946         return FALSE;
947
948     while (PROFILE_EnumWineIniString( "afmfiles", idx++, key, sizeof(key),
949             value, sizeof(value)))
950     {
951         AFM* afm = PSDRV_AFMParse(value);
952         
953         if (afm) {
954             if(afm->EncodingScheme && 
955                !strcmp(afm->EncodingScheme, "AdobeStandardEncoding")) {
956                 PSDRV_ReencodeCharWidths(afm);
957             }
958             if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm) == FALSE) {
959                 return FALSE;
960             }
961         }
962         else {
963             WARN("Error parsing %s\n", value);
964         }
965     }
966
967     for (idx = 0; PROFILE_EnumWineIniString ("afmdirs", idx, key, sizeof (key),
968             value, sizeof (value)); ++idx)
969         if (PSDRV_ReadAFMDir (value) == FALSE)
970             return FALSE;
971
972     PSDRV_IndexGlyphList();             /* So SortFontMetrics will work */
973     if (SortFontMetrics() == FALSE)
974         return FALSE;
975     CalcWindowsMetrics();
976
977 #ifdef HAVE_FREETYPE   
978     if (PSDRV_GetTrueTypeMetrics() == FALSE)
979         return FALSE;
980     PSDRV_IndexGlyphList();
981 #endif
982
983     PSDRV_DumpFontList();
984     return TRUE;
985 }