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