When including 'wine/port.h', include it first.
[wine] / dlls / wineps / type1afm.c
1 /*******************************************************************************
2  *  Adobe Font Metric (AFM) file parsing finctions for Wine PostScript driver.
3  *  See http://partners.adobe.com/asn/developer/pdfs/tn/5004.AFM_Spec.pdf.
4  *
5  *  Copyright 2001  Ian Pilcher
6  *
7  *
8  *  NOTE:  Many of the functions in this file can return either fatal errors
9  *      (memory allocation failure) or non-fatal errors (unusable AFM file).
10  *      Fatal errors are indicated by returning FALSE; see individual function
11  *      descriptions for how they indicate non-fatal errors.
12  *
13  */
14  
15 #include "config.h"
16
17 #include <string.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <dirent.h>
21 #include <errno.h>
22 #include <ctype.h>
23 #include <limits.h>         /* INT_MIN */
24
25 #ifdef HAVE_FLOAT_H
26 #include <float.h>          /* FLT_MAX */
27 #endif
28
29 #include "winnt.h"
30 #include "winerror.h"
31 #include "winreg.h"
32 #include "psdrv.h"
33 #include "debugtools.h"
34
35 DEFAULT_DEBUG_CHANNEL(psdrv);
36
37 /*******************************************************************************
38  *  ReadLine
39  *
40  *  Reads a line from a text file into the buffer and trims trailing whitespace.
41  *  Can handle DOS and Unix text files, including weird DOS EOF.  Returns FALSE
42  *  for unexpected I/O errors; otherwise returns TRUE and sets *p_result to
43  *  either the number of characters in the returned string or one of the
44  *  following:
45  *
46  *      0:          Blank (or all whitespace) line.  This is just a special case
47  *                  of the normal behavior.
48  *
49  *      EOF:        End of file has been reached.
50  *
51  *      INT_MIN:    Buffer overflow.  Returned string is truncated (duh!) and
52  *                  trailing whitespace is *not* trimmed.  Remaining text in
53  *                  line is discarded.  (I.e. the file pointer is positioned at
54  *                  the beginning of the next line.)
55  *
56  */
57 static BOOL ReadLine(FILE *file, CHAR buffer[], INT bufsize, INT *p_result)
58 {
59      CHAR   *cp;
60      INT    i;
61      
62      if (fgets(buffer, bufsize, file) == NULL)
63      {
64         if (feof(file) == 0)                            /* EOF or error? */
65         {
66             ERR("%s\n", strerror(errno));
67             return FALSE;
68         }
69         
70         *p_result = EOF;
71         return TRUE;
72     }
73     
74     cp = strchr(buffer, '\n');
75     if (cp == NULL)
76     {
77         i = strlen(buffer);
78         
79         if (i == bufsize - 1)       /* max possible; was line truncated? */
80         {
81             do
82                 i = fgetc(file);                /* find the newline or EOF */
83             while (i != '\n' && i != EOF);
84             
85             if (i == EOF)
86             {
87                 if (feof(file) == 0)                    /* EOF or error? */
88                 {
89                     ERR("%s\n", strerror(errno));
90                     return FALSE;
91                 }
92                 
93                 WARN("No newline at EOF\n");
94             }
95             
96             *p_result = INT_MIN;
97             return TRUE;
98         }
99         else                            /* no newline and not truncated */
100         {
101             if (strcmp(buffer, "\x1a") == 0)        /* test for DOS EOF */
102             {
103                 *p_result = EOF;
104                 return TRUE;
105             }
106             
107             WARN("No newline at EOF\n");
108             cp = buffer + i;    /* points to \0 where \n should have been */
109         }
110     }
111     
112     do
113     {
114         *cp = '\0';                             /* trim trailing whitespace */
115         if (cp == buffer)
116             break;                              /* don't underflow buffer */
117         --cp;
118     }
119     while (isspace(*cp));
120     
121     *p_result = strlen(buffer);
122     return TRUE;
123 }
124
125 /*******************************************************************************
126  *  FindLine
127  *
128  *  Finds a line in the file that begins with the given string.  Returns FALSE
129  *  for unexpected I/O errors; returns an empty (zero character) string if the
130  *  requested line is not found.
131  *
132  *  NOTE:  The file pointer *MUST* be positioned at the beginning of a line when
133  *      this function is called.  Otherwise, an infinite loop can result.
134  *
135  */
136 static BOOL FindLine(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key)
137 {
138     INT     len = strlen(key);
139     LONG    start = ftell(file);
140     
141     do
142     {
143         INT     result;
144         BOOL    ok;
145         
146         ok = ReadLine(file, buffer, bufsize, &result);
147         if (ok == FALSE)
148             return FALSE;
149             
150         if (result > 0 && strncmp(buffer, key, len) == 0)
151             return TRUE;
152             
153         if (result == EOF)
154         {
155             rewind(file);
156         }
157         else if (result == INT_MIN)
158         {
159             WARN("Line beginning '%32s...' is too long; ignoring\n", buffer);
160         }
161     }
162     while (ftell(file) != start);
163     
164     WARN("Couldn't find line '%s...' in AFM file\n", key);
165     buffer[0] = '\0';
166     return TRUE;
167 }
168
169 /*******************************************************************************
170  *  DoubleToFloat
171  *
172  *  Utility function to convert double to float while checking for overflow.
173  *  Will also catch strtod overflows, since HUGE_VAL > FLT_MAX (at least on
174  *  Linux x86/gcc).
175  *
176  */
177 inline static BOOL DoubleToFloat(float *p_f, double d)
178 {
179     if (d > (double)FLT_MAX || d < -(double)FLT_MAX)
180         return FALSE;
181         
182     *p_f = (float)d;
183     return TRUE;
184 }
185
186 /*******************************************************************************
187  *  Round
188  *
189  *  Utility function to add or subtract 0.5 before converting to integer type.
190  *
191  */
192 inline static float Round(float f)
193 {
194     return (f >= 0.0) ? (f + 0.5) : (f - 0.5);
195 }
196
197 /*******************************************************************************
198  *  ReadFloat
199  *
200  *  Finds and parses a line of the form '<key> <value>', where value is a
201  *  number.  Sets *p_found to FALSE if a corresponding line cannot be found, or
202  *  it cannot be parsed; also sets *p_ret to 0.0, so calling functions can just
203  *  skip the check of *p_found if the item is not required.
204  *
205  */
206 static BOOL ReadFloat(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
207         FLOAT *p_ret, BOOL *p_found)
208 {
209     CHAR    *cp, *end_ptr;
210     double  d;
211
212     if (FindLine(file, buffer, bufsize, key) == FALSE)
213         return FALSE;
214         
215     if (buffer[0] == '\0')          /* line not found */
216     {
217         *p_found = FALSE;
218         *p_ret = 0.0;
219         return TRUE;
220     }
221     
222     cp = buffer + strlen(key);                      /* first char after key */
223     errno = 0;
224     d = strtod(cp, &end_ptr);
225     
226     if (end_ptr == cp || errno != 0 || DoubleToFloat(p_ret, d) == FALSE)
227     {
228         WARN("Error parsing line '%s'\n", buffer); 
229         *p_found = FALSE;
230         *p_ret = 0.0;
231         return TRUE;
232     }
233     
234     *p_found = TRUE;
235     return TRUE;
236 }
237
238 /*******************************************************************************
239  *  ReadInt
240  *
241  *  See description of ReadFloat.
242  *
243  */
244 static BOOL ReadInt(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
245         INT *p_ret, BOOL *p_found)
246 {
247     BOOL    retval;
248     FLOAT   f;
249
250     retval = ReadFloat(file, buffer, bufsize, key, &f, p_found);
251     if (retval == FALSE || *p_found == FALSE)
252     {
253         *p_ret = 0;
254         return retval;
255     }
256     
257     f = Round(f);
258     
259     if (f > (FLOAT)INT_MAX || f < (FLOAT)INT_MIN)
260     {
261         WARN("Error parsing line '%s'\n", buffer);
262         *p_ret = 0;
263         *p_found = FALSE;
264         return TRUE;
265     }
266     
267     *p_ret = (INT)f;
268     return TRUE;
269 }
270
271 /*******************************************************************************
272  *  ReadString
273  *
274  *  Returns FALSE on I/O error or memory allocation failure; sets *p_str to NULL
275  *  if line cannot be found or can't be parsed.
276  *
277  */
278 static BOOL ReadString(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
279         LPSTR *p_str)
280 {
281     CHAR    *cp;
282
283     if (FindLine(file, buffer, bufsize, key) == FALSE)
284         return FALSE;
285         
286     if (buffer[0] == '\0')
287     {
288         *p_str = NULL;
289         return TRUE;
290     }
291     
292     cp = buffer + strlen(key);                      /* first char after key */
293     if (*cp == '\0')
294     {
295         *p_str = NULL;
296         return TRUE;
297     }
298     
299     while (isspace(*cp))                /* find first non-whitespace char */
300         ++cp;
301     
302     *p_str = HeapAlloc(PSDRV_Heap, 0, strlen(cp) + 1);
303     if (*p_str == NULL)
304         return FALSE;
305         
306     strcpy(*p_str, cp);
307     return TRUE;
308 }
309
310 /*******************************************************************************
311  *  ReadBBox
312  *
313  *  Similar to ReadFloat above.
314  *
315  */
316 static BOOL ReadBBox(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
317         BOOL *p_found)
318 {
319     CHAR    *cp, *end_ptr;
320     double  d;
321
322     if (FindLine(file, buffer, bufsize, "FontBBox") == FALSE)
323         return FALSE;
324         
325     if (buffer[0] == '\0')
326     {
327         *p_found = FALSE;
328         return TRUE;
329     }
330     
331     errno = 0;
332     
333     cp = buffer + sizeof("FontBBox");
334     d = strtod(cp, &end_ptr);
335     if (end_ptr == cp || errno != 0 ||
336             DoubleToFloat(&(afm->FontBBox.llx), d) == FALSE)
337         goto parse_error;
338
339     cp = end_ptr;
340     d = strtod(cp, &end_ptr);
341     if (end_ptr == cp || errno != 0 ||
342             DoubleToFloat(&(afm->FontBBox.lly), d) == FALSE)
343         goto parse_error;
344         
345     cp = end_ptr;
346     d = strtod(cp, &end_ptr);
347     if (end_ptr == cp || errno != 0
348             || DoubleToFloat(&(afm->FontBBox.urx), d) == FALSE)
349         goto parse_error;
350         
351     cp = end_ptr;
352     d = strtod(cp, &end_ptr);
353     if (end_ptr == cp || errno != 0
354             || DoubleToFloat(&(afm->FontBBox.ury), d) == FALSE)
355         goto parse_error;
356         
357     *p_found = TRUE;
358     return TRUE;
359     
360     parse_error:
361         WARN("Error parsing line '%s'\n", buffer);
362         *p_found = FALSE;
363         return TRUE;
364 }
365
366 /*******************************************************************************
367  *  ReadWeight
368  *
369  *  Finds and parses the 'Weight' line of an AFM file.  Only tries to determine
370  *  if a font is bold (FW_BOLD) or not (FW_NORMAL) -- ignoring all those cute
371  *  little FW_* typedefs in the Win32 doc.  AFAICT, this is what the Windows
372  *  PostScript driver does.
373  *
374  */
375 static const struct { LPCSTR keyword; INT weight; } afm_weights[] =
376 {
377     { "REGULAR",        FW_NORMAL },
378     { "ROMAN",          FW_NORMAL },
379     { "BOLD",           FW_BOLD },
380     { "BOOK",           FW_NORMAL },
381     { "MEDIUM",         FW_NORMAL },
382     { "LIGHT",          FW_NORMAL },
383     { "BLACK",          FW_BOLD },
384     { "HEAVY",          FW_BOLD },
385     { "DEMI",           FW_BOLD },
386     { "ULTRA",          FW_BOLD },
387     { "SUPER" ,         FW_BOLD },
388     { NULL,             0 }
389 };
390  
391 static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
392         BOOL *p_found)
393 {
394     LPSTR   sz;
395     CHAR    *cp;
396     INT     i;
397     
398     if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE)
399         return FALSE;
400
401     if (sz == NULL)
402     {
403         *p_found = FALSE;
404         return TRUE;
405     }
406         
407     for (cp = sz; *cp != '\0'; ++cp)
408         *cp = toupper(*cp);
409         
410     for (i = 0; afm_weights[i].keyword != NULL; ++i)
411     {
412         if (strstr(sz, afm_weights[i].keyword) != NULL)
413         {
414             afm->Weight = afm_weights[i].weight;
415             *p_found = TRUE;
416             HeapFree(PSDRV_Heap, 0, sz);
417             return TRUE;
418         }
419     }
420     
421     WARN("Unknown weight '%s'; treating as Roman\n", sz);
422     
423     afm->Weight = FW_NORMAL;
424     *p_found = TRUE;
425     HeapFree(PSDRV_Heap, 0, sz);
426     return TRUE;
427 }
428
429 /*******************************************************************************
430  *  ReadFixedPitch
431  *
432  */
433 static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
434         BOOL *p_found)
435 {
436     LPSTR   sz;
437     
438     if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE)
439         return FALSE;
440         
441     if (sz == NULL)
442     {
443         *p_found = FALSE;
444         return TRUE;
445     }
446
447     if (strcasecmp(sz, "false") == 0)
448     {
449         afm->IsFixedPitch = FALSE;
450         *p_found = TRUE;
451         HeapFree(PSDRV_Heap, 0, sz);
452         return TRUE;
453     }
454     
455     if (strcasecmp(sz, "true") == 0)
456     {
457         afm->IsFixedPitch = TRUE;
458         *p_found = TRUE;
459         HeapFree(PSDRV_Heap, 0, sz);
460         return TRUE;
461     }
462     
463     WARN("Can't parse line '%s'\n", buffer);
464     
465     *p_found = FALSE;
466     HeapFree(PSDRV_Heap, 0, sz);
467     return TRUE;
468 }
469
470 /*******************************************************************************
471  *  ReadFontMetrics
472  *
473  *  Allocates space for the AFM on the driver heap and reads basic font metrics.
474  *  Returns FALSE for memory allocation failure; sets *p_afm to NULL if AFM file
475  *  is unusable.
476  *
477  */
478 static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm)
479 {
480     AFM     *afm;
481     BOOL    retval, found;
482
483     *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
484     if (afm == NULL)
485         return FALSE;
486         
487     retval = ReadWeight(file, buffer, bufsize, afm, &found);
488     if (retval == FALSE || found == FALSE)
489         goto cleanup_afm;
490         
491     retval = ReadFloat(file, buffer, bufsize, "ItalicAngle",
492             &(afm->ItalicAngle), &found);
493     if (retval == FALSE || found == FALSE)
494         goto cleanup_afm;
495         
496     retval = ReadFixedPitch(file, buffer, bufsize, afm, &found);
497     if (retval == FALSE || found == FALSE)
498         goto cleanup_afm;
499         
500     retval = ReadBBox(file, buffer, bufsize, afm, &found);
501     if (retval == FALSE || found == FALSE)
502         goto cleanup_afm;
503         
504     retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition",
505             &(afm->UnderlinePosition), &found);
506     if (retval == FALSE || found == FALSE)
507         goto cleanup_afm;
508         
509     retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness",
510             &(afm->UnderlineThickness), &found);
511     if (retval == FALSE || found == FALSE)
512         goto cleanup_afm;
513         
514     retval = ReadFloat(file, buffer, bufsize, "Ascender",       /* optional */
515             &(afm->Ascender), &found);
516     if (retval == FALSE)
517         goto cleanup_afm;
518         
519     retval = ReadFloat(file, buffer, bufsize, "Descender",      /* optional */
520             &(afm->Descender), &found);
521     if (retval == FALSE)
522         goto cleanup_afm;
523         
524     afm->WinMetrics.usUnitsPerEm = 1000;
525     afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender);
526     afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender);
527     
528     if (afm->WinMetrics.sTypoAscender == 0)
529         afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury);
530         
531     if (afm->WinMetrics.sTypoDescender == 0)
532         afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly);
533         
534     afm->WinMetrics.sTypoLineGap = 1200 -
535             (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender);
536     if (afm->WinMetrics.sTypoLineGap < 0)
537         afm->WinMetrics.sTypoLineGap = 0;
538         
539     return TRUE;
540         
541     cleanup_afm:                        /* handle fatal or non-fatal errors */
542         HeapFree(PSDRV_Heap, 0, afm);
543         *p_afm = NULL;
544         return retval;
545 }
546
547 /*******************************************************************************
548  *  ParseC
549  *
550  *  Fatal error:        return FALSE (none defined)
551  *
552  *  Non-fatal error:    leave metrics->C set to INT_MAX
553  *
554  */
555 static BOOL ParseC(LPSTR sz, OLD_AFMMETRICS *metrics)
556 {
557     int     base = 10;
558     long    l;
559     CHAR    *cp, *end_ptr;
560
561     cp = sz + 1;
562     
563     if (*cp == 'H')
564     {
565         base = 16;
566         ++cp;
567     }
568     
569     errno = 0;
570     l = strtol(cp, &end_ptr, base);
571     if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN)
572     {
573         WARN("Error parsing character code '%s'\n", sz);
574         return TRUE;
575     }
576     
577     metrics->C = (INT)l;
578     return TRUE;
579 }
580
581 /*******************************************************************************
582  *  ParseW
583  *
584  *  Fatal error:        return FALSE (none defined)
585  *
586  *  Non-fatal error:    leave metrics->WX set to FLT_MAX
587  *
588  */
589 static BOOL ParseW(LPSTR sz, OLD_AFMMETRICS *metrics)
590 {
591     CHAR    *cp, *end_ptr;
592     BOOL    vector = TRUE;
593     double  d;
594
595     cp = sz + 1;
596     
597     if (*cp == '0')
598         ++cp;
599         
600     if (*cp == 'X')
601     {
602         vector = FALSE;
603         ++cp;
604     }
605     
606     if (!isspace(*cp))
607         goto parse_error;
608         
609     errno = 0;
610     d = strtod(cp, &end_ptr);
611     if (end_ptr == cp || errno != 0 ||
612             DoubleToFloat(&(metrics->WX), d) == FALSE)
613         goto parse_error;
614         
615     if (vector == FALSE)
616         return TRUE;
617         
618     /*  Make sure that Y component of vector is zero */
619         
620     d = strtod(cp, &end_ptr);                               /* errno == 0 */
621     if (end_ptr == cp || errno != 0 || d != 0.0)
622     {
623         metrics->WX = FLT_MAX;
624         goto parse_error;
625     }
626         
627     return TRUE;
628         
629     parse_error:
630         WARN("Error parsing character width '%s'\n", sz);
631         return TRUE;
632 }
633
634 /*******************************************************************************
635  *
636  *  ParseB
637  *
638  *  Fatal error:        return FALSE (none defined)
639  *
640  *  Non-fatal error:    leave metrics->B.ury set to FLT_MAX
641  *
642  */
643 static BOOL ParseB(LPSTR sz, OLD_AFMMETRICS *metrics)
644 {
645     CHAR    *cp, *end_ptr;
646     double  d;
647     
648     errno = 0;
649     
650     cp = sz + 1;
651     d = strtod(cp, &end_ptr);
652     if (end_ptr == cp || errno != 0 ||
653             DoubleToFloat(&(metrics->B.llx), d) == FALSE)
654         goto parse_error;
655         
656     cp = end_ptr;
657     d = strtod(cp, &end_ptr);
658     if (end_ptr == cp || errno != 0 ||
659             DoubleToFloat(&(metrics->B.lly), d) == FALSE)
660         goto parse_error;
661         
662     cp = end_ptr;
663     d = strtod(cp, &end_ptr);
664     if (end_ptr == cp || errno != 0 ||
665             DoubleToFloat(&(metrics->B.urx), d) == FALSE)
666         goto parse_error;
667
668     cp = end_ptr;
669     d = strtod(cp, &end_ptr);
670     if (end_ptr == cp || errno != 0 ||
671             DoubleToFloat(&(metrics->B.ury), d) == FALSE)
672         goto parse_error;
673
674     return TRUE;
675     
676     parse_error:
677         WARN("Error parsing glyph bounding box '%s'\n", sz);
678         return TRUE;
679 }
680
681 /*******************************************************************************
682  *  ParseN
683  *
684  *  Fatal error:        return FALSE (PSDRV_GlyphName failure)
685  *
686  *  Non-fatal error:    leave metrics-> set to NULL
687  *
688  */
689 static BOOL ParseN(LPSTR sz, OLD_AFMMETRICS *metrics)
690 {
691     CHAR    save, *cp, *end_ptr;
692     
693     cp = sz + 1;
694     
695     while (isspace(*cp))
696         ++cp;
697         
698     end_ptr = cp;
699     
700     while (*end_ptr != '\0' && !isspace(*end_ptr))
701         ++end_ptr;
702         
703     if (end_ptr == cp)
704     {
705         WARN("Error parsing glyph name '%s'\n", sz);
706         return TRUE;
707     }
708         
709     save = *end_ptr;
710     *end_ptr = '\0';
711     
712     metrics->N = PSDRV_GlyphName(cp);
713     if (metrics->N == NULL)
714         return FALSE;
715         
716     *end_ptr = save;
717     return TRUE;
718 }
719
720 /*******************************************************************************
721  *  ParseCharMetrics
722  *
723  *  Parses the metrics line for a single glyph in an AFM file.  Returns FALSE on
724  *  fatal error; sets *metrics to 'badmetrics' on non-fatal error.
725  *
726  */
727 static const OLD_AFMMETRICS badmetrics =
728 {
729     INT_MAX,                                        /* C */
730     LONG_MAX,                                       /* UV */
731     FLT_MAX,                                        /* WX */
732     NULL,                                           /* N */
733     { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX },         /* B */
734     NULL                                            /* L */
735 };
736  
737 static BOOL ParseCharMetrics(LPSTR buffer, INT len, OLD_AFMMETRICS *metrics)
738 {
739     CHAR    *cp = buffer;
740
741     *metrics = badmetrics;
742     
743     while (*cp != '\0')
744     {
745         while (isspace(*cp))
746             ++cp;
747             
748         switch(*cp)
749         {
750             case 'C':   if (ParseC(cp, metrics) == FALSE)
751                             return FALSE;
752                         break;
753                         
754             case 'W':   if (ParseW(cp, metrics) == FALSE)
755                             return FALSE;
756                         break;
757                         
758             case 'N':   if (ParseN(cp, metrics) == FALSE)
759                             return FALSE;
760                         break;
761                         
762             case 'B':   if (ParseB(cp, metrics) == FALSE)
763                             return FALSE;
764                         break;
765         }
766         
767         cp = strchr(cp, ';');
768         if (cp == NULL)
769         {
770             WARN("No terminating semicolon\n");
771             break;
772         }
773         
774         ++cp;
775     }
776     
777     if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL ||
778             metrics->B.ury == FLT_MAX)
779     {
780         *metrics = badmetrics;
781         return TRUE;
782     }
783
784     return TRUE;
785 }
786
787 /*******************************************************************************
788  *  IsWinANSI
789  *
790  *  Checks whether Unicode value is part of Microsoft code page 1252
791  *
792  */
793 static const LONG ansiChars[21] =
794 {
795     0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
796     0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
797     0x20ac, 0x2122, 0x2219
798 };
799
800 static int cmpUV(const void *a, const void *b)
801 {
802     return (int)(*((const LONG *)a) - *((const LONG *)b));
803 }
804  
805 inline static BOOL IsWinANSI(LONG uv)
806 {
807     if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) ||
808             (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) ||
809             (0x2020 <= uv && uv <= 0x2022))
810         return TRUE;
811         
812     if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL)
813         return TRUE;
814         
815     return FALSE;
816 }
817
818 /*******************************************************************************
819  *  Unicodify
820  *
821  *  Determines Unicode value (UV) for each glyph, based on font encoding.
822  *
823  *      FontSpecific:   Usable encodings (0x20 - 0xff) are mapped into the
824  *                      Unicode private use range U+F020 - U+F0FF.
825  *
826  *      other:          UV determined by glyph name, based on Adobe Glyph List.
827  *
828  *  Also does some font metric calculations that require UVs to be known.
829  *
830  */
831 static int UnicodeGlyphByNameIndex(const void *a, const void *b)
832 {
833     return ((const UNICODEGLYPH *)a)->name->index -
834             ((const UNICODEGLYPH *)b)->name->index;
835 }
836  
837 static VOID Unicodify(AFM *afm, OLD_AFMMETRICS *metrics)
838 {
839     INT     i;
840     
841     if (strcmp(afm->EncodingScheme, "FontSpecific") == 0)
842     {
843         for (i = 0; i < afm->NumofMetrics; ++i)
844         {
845             if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff)
846             {
847                 metrics[i].UV = ((LONG)(metrics[i].C)) | 0xf000L;
848             }
849             else
850             {
851                 TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz);
852                 metrics[i].UV = -1L;
853             }
854         }
855         
856         afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
857         afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
858     }
859     else                                        /* non-FontSpecific encoding */
860     {
861         UNICODEGLYPH    ug, *p_ug;
862         
863         PSDRV_IndexGlyphList();         /* for fast searching of glyph names */
864         
865         afm->WinMetrics.sAscender = afm->WinMetrics.sDescender = 0;
866         
867         for (i = 0; i < afm->NumofMetrics; ++i)
868         {
869             ug.name = metrics[i].N;
870             p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
871                     sizeof(ug), UnicodeGlyphByNameIndex);
872             if (p_ug == NULL)
873             {
874                 TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz);
875                 metrics[i].UV = -1L;
876             }
877             else
878             {
879                 metrics[i].UV = p_ug->UV;
880                 
881                 if (IsWinANSI(p_ug->UV))
882                 {
883                     SHORT   ury = (SHORT)Round(metrics[i].B.ury);
884                     SHORT   lly = (SHORT)Round(metrics[i].B.lly);
885                     
886                     if (ury > afm->WinMetrics.sAscender)
887                         afm->WinMetrics.sAscender = ury;
888                     if (lly < afm->WinMetrics.sDescender)
889                         afm->WinMetrics.sDescender = lly;
890                 }
891             }
892         }
893         
894         if (afm->WinMetrics.sAscender == 0)
895             afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
896         if (afm->WinMetrics.sDescender == 0)
897             afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
898     }
899     
900     afm->WinMetrics.sLineGap =
901             1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender);
902     if (afm->WinMetrics.sLineGap < 0)
903         afm->WinMetrics.sLineGap = 0;
904     
905     afm->WinMetrics.usWinAscent = (afm->WinMetrics.sAscender > 0) ?
906             afm->WinMetrics.sAscender : 0;
907     afm->WinMetrics.usWinDescent = (afm->WinMetrics.sDescender < 0) ?
908             -(afm->WinMetrics.sDescender) : 0;
909 }
910
911 /*******************************************************************************
912  *  ReadCharMetrics
913  *
914  *  Reads metrics for all glyphs.  *p_metrics will be NULL on non-fatal error.
915  *
916  */
917 static int OldAFMMetricsByUV(const void *a, const void *b)
918 {
919     return ((const OLD_AFMMETRICS *)a)->UV - ((const OLD_AFMMETRICS *)b)->UV;
920
921  
922 static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
923         AFMMETRICS **p_metrics)
924 {
925     BOOL            retval, found;
926     OLD_AFMMETRICS  *old_metrics, *encoded_metrics;
927     AFMMETRICS      *metrics;
928     INT             i, len;
929     
930     retval = ReadInt(file, buffer, bufsize, "StartCharMetrics",
931             &(afm->NumofMetrics), &found);
932     if (retval == FALSE || found == FALSE)
933     {
934         *p_metrics = NULL;
935         return retval;
936     }
937     
938     old_metrics = HeapAlloc(PSDRV_Heap, 0,
939             afm->NumofMetrics * sizeof(*old_metrics));
940     if (old_metrics == NULL)
941         return FALSE;
942         
943     for (i = 0; i < afm->NumofMetrics; ++i)
944     {
945         retval = ReadLine(file, buffer, bufsize, &len);
946         if (retval == FALSE)
947             goto cleanup_old_metrics;
948         
949         if(len > 0)
950         {
951             retval = ParseCharMetrics(buffer, len, old_metrics + i);
952             if (retval == FALSE || old_metrics[i].C == INT_MAX)
953                 goto cleanup_old_metrics;
954                 
955             continue;
956         }
957         
958         switch (len)
959         {
960             case 0:         --i;
961                             continue;
962                                 
963             case INT_MIN:   WARN("Ignoring long line '%32s...'\n", buffer);
964                             goto cleanup_old_metrics;       /* retval == TRUE */
965                                 
966             case EOF:       WARN("Unexpected EOF\n");
967                             goto cleanup_old_metrics;       /* retval == TRUE */
968         }
969     }
970     
971     Unicodify(afm, old_metrics);    /* wait until glyph names have been read */
972             
973     qsort(old_metrics, afm->NumofMetrics, sizeof(*old_metrics),
974             OldAFMMetricsByUV);
975     
976     for (i = 0; old_metrics[i].UV == -1; ++i);      /* count unencoded glyphs */
977     
978     afm->NumofMetrics -= i;
979     encoded_metrics = old_metrics + i;
980     
981     afm->Metrics = *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0,
982             afm->NumofMetrics * sizeof(*metrics));
983     if (afm->Metrics == NULL)
984         goto cleanup_old_metrics;                           /* retval == TRUE */
985         
986     for (i = 0; i < afm->NumofMetrics; ++i, ++metrics, ++encoded_metrics)
987     {
988         metrics->C = encoded_metrics->C;
989         metrics->UV = encoded_metrics->UV;
990         metrics->WX = encoded_metrics->WX;
991         metrics->N = encoded_metrics->N;
992     }
993     
994     HeapFree(PSDRV_Heap, 0, old_metrics);
995     
996     afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
997     
998     return TRUE;
999     
1000     cleanup_old_metrics:                /* handle fatal or non-fatal errors */
1001         HeapFree(PSDRV_Heap, 0, old_metrics);
1002         *p_metrics = NULL;
1003         return retval;
1004 }
1005
1006 /*******************************************************************************
1007  *  BuildAFM
1008  *
1009  *  Builds the AFM for a PostScript font and adds it to the driver font list.
1010  *  Returns FALSE only on an unexpected error (memory allocation or I/O error). 
1011  *
1012  */
1013 static BOOL BuildAFM(FILE *file)
1014 {
1015     CHAR        buffer[258];            /* allow for <cr>, <lf>, and <nul> */
1016     AFM         *afm;
1017     AFMMETRICS  *metrics;
1018     LPSTR       font_name, full_name, family_name, encoding_scheme;
1019     BOOL        retval, added;
1020     
1021     retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm);
1022     if (retval == FALSE || afm == NULL)
1023         return retval;
1024         
1025     retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name);
1026     if (retval == FALSE || font_name == NULL)
1027         goto cleanup_afm;
1028         
1029     retval = ReadString(file, buffer, sizeof(buffer), "FullName", &full_name);
1030     if (retval == FALSE || full_name == NULL)
1031         goto cleanup_font_name;
1032         
1033     retval = ReadString(file, buffer, sizeof(buffer), "FamilyName",
1034             &family_name);
1035     if (retval == FALSE || family_name == NULL)
1036         goto cleanup_full_name;
1037         
1038     retval = ReadString(file, buffer, sizeof(buffer), "EncodingScheme",
1039             &encoding_scheme);
1040     if (retval == FALSE || encoding_scheme == NULL)
1041         goto cleanup_family_name;
1042     
1043     afm->FontName = font_name;
1044     afm->FullName = full_name;
1045     afm->FamilyName = family_name;
1046     afm->EncodingScheme = encoding_scheme;
1047         
1048     retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics);
1049     if (retval == FALSE || metrics == FALSE)
1050         goto cleanup_encoding_scheme;
1051         
1052     retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
1053     if (retval == FALSE || added == FALSE)
1054         goto cleanup_encoding_scheme;
1055         
1056     return TRUE;
1057     
1058     /* clean up after fatal or non-fatal errors */
1059     
1060     cleanup_encoding_scheme:
1061         HeapFree(PSDRV_Heap, 0, encoding_scheme);
1062     cleanup_family_name:
1063         HeapFree(PSDRV_Heap, 0, family_name);
1064     cleanup_full_name:
1065         HeapFree(PSDRV_Heap, 0, full_name);
1066     cleanup_font_name:
1067         HeapFree(PSDRV_Heap, 0, font_name);
1068     cleanup_afm:
1069         HeapFree(PSDRV_Heap, 0, afm);
1070         
1071     return retval;
1072 }
1073
1074 /*******************************************************************************
1075  *  ReadAFMFile
1076  *
1077  *  Reads font metrics from Type 1 AFM file.  Only returns FALSE for
1078  *  unexpected errors (memory allocation or I/O).
1079  *
1080  */
1081 static BOOL ReadAFMFile(LPCSTR filename)
1082 {
1083     FILE    *f;
1084     BOOL    retval;
1085
1086     TRACE("%s\n", filename);
1087     
1088     f = fopen(filename, "r");
1089     if (f == NULL)
1090     {
1091         WARN("%s: %s\n", filename, strerror(errno));
1092         return TRUE;
1093     }
1094     
1095     retval = BuildAFM(f);
1096     
1097     fclose(f);
1098     return retval;
1099 }
1100
1101 /*******************************************************************************
1102  *  ReadAFMDir
1103  *
1104  *  Reads all Type 1 AFM files in a directory.
1105  *
1106  */
1107 static BOOL ReadAFMDir(LPCSTR dirname)
1108 {
1109     struct dirent   *dent;
1110     DIR             *dir;
1111     CHAR            filename[256];
1112     
1113     dir = opendir(dirname);
1114     if (dir == NULL)
1115     {
1116         WARN("%s: %s\n", dirname, strerror(errno));
1117         return TRUE;
1118     }
1119     
1120     while ((dent = readdir(dir)) != NULL)
1121     {
1122         CHAR    *file_extension = strchr(dent->d_name, '.');
1123         int     fn_len;
1124         
1125         if (file_extension == NULL || strcasecmp(file_extension, ".afm") != 0)
1126             continue;
1127             
1128         fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
1129         if (fn_len < 0 || fn_len > sizeof(filename) - 1)
1130         {
1131             WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
1132             continue;
1133         }
1134         
1135         if (ReadAFMFile(filename) == FALSE)
1136         {
1137             closedir(dir);
1138             return FALSE;
1139         }
1140     }
1141     
1142     closedir(dir);
1143     return TRUE;
1144 }
1145
1146 /*******************************************************************************
1147  *  PSDRV_GetType1Metrics
1148  *
1149  *  Reads font metrics from Type 1 AFM font files in directories listed in the
1150  *  [afmdirs] section of the Wine configuration file.
1151  *
1152  *  If this function fails (returns FALSE), the dirver will fail to initialize
1153  *  and the driver heap will be destroyed, so it's not necessary to HeapFree
1154  *  everything in that event.
1155  *
1156  */
1157 BOOL PSDRV_GetType1Metrics(void)
1158 {
1159     CHAR    name_buf[256], value_buf[256];
1160     INT     i = 0;
1161     HKEY    hkey;
1162     DWORD   type, name_len, value_len;
1163     
1164     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1165             "Software\\Wine\\Wine\\Config\\afmdirs",
1166             0, KEY_READ, &hkey) != ERROR_SUCCESS)
1167         return TRUE;
1168         
1169     name_len = sizeof(name_buf);
1170     value_len = sizeof(value_buf);
1171     
1172     while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf,
1173             &value_len) == ERROR_SUCCESS)
1174     {
1175         value_buf[sizeof(value_buf) - 1] = '\0';
1176         
1177         if (ReadAFMDir(value_buf) == FALSE)
1178         {
1179             RegCloseKey(hkey);
1180             return FALSE;
1181         }
1182         
1183         /* initialize lengths for new iteration */
1184         
1185         name_len = sizeof(name_buf);
1186         value_len = sizeof(value_buf);
1187     }
1188     
1189     RegCloseKey(hkey);
1190     return TRUE;
1191 }