Added afm weight "NORMAL".
[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     { "NORMAL",         FW_NORMAL },
392     { "ROMAN",          FW_NORMAL },
393     { "BOLD",           FW_BOLD },
394     { "BOOK",           FW_NORMAL },
395     { "MEDIUM",         FW_NORMAL },
396     { "LIGHT",          FW_NORMAL },
397     { "BLACK",          FW_BOLD },
398     { "HEAVY",          FW_BOLD },
399     { "DEMI",           FW_BOLD },
400     { "ULTRA",          FW_BOLD },
401     { "SUPER" ,         FW_BOLD },
402     { NULL,             0 }
403 };
404
405 static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
406         BOOL *p_found)
407 {
408     LPSTR   sz;
409     CHAR    *cp;
410     INT     i;
411
412     if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE)
413         return FALSE;
414
415     if (sz == NULL)
416     {
417         *p_found = FALSE;
418         return TRUE;
419     }
420
421     for (cp = sz; *cp != '\0'; ++cp)
422         *cp = toupper(*cp);
423
424     for (i = 0; afm_weights[i].keyword != NULL; ++i)
425     {
426         if (strstr(sz, afm_weights[i].keyword) != NULL)
427         {
428             afm->Weight = afm_weights[i].weight;
429             *p_found = TRUE;
430             HeapFree(PSDRV_Heap, 0, sz);
431             return TRUE;
432         }
433     }
434
435     WARN("Unknown weight '%s'; treating as Roman\n", sz);
436
437     afm->Weight = FW_NORMAL;
438     *p_found = TRUE;
439     HeapFree(PSDRV_Heap, 0, sz);
440     return TRUE;
441 }
442
443 /*******************************************************************************
444  *  ReadFixedPitch
445  *
446  */
447 static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
448         BOOL *p_found)
449 {
450     LPSTR   sz;
451
452     if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE)
453         return FALSE;
454
455     if (sz == NULL)
456     {
457         *p_found = FALSE;
458         return TRUE;
459     }
460
461     if (strcasecmp(sz, "false") == 0)
462     {
463         afm->IsFixedPitch = FALSE;
464         *p_found = TRUE;
465         HeapFree(PSDRV_Heap, 0, sz);
466         return TRUE;
467     }
468
469     if (strcasecmp(sz, "true") == 0)
470     {
471         afm->IsFixedPitch = TRUE;
472         *p_found = TRUE;
473         HeapFree(PSDRV_Heap, 0, sz);
474         return TRUE;
475     }
476
477     WARN("Can't parse line '%s'\n", buffer);
478
479     *p_found = FALSE;
480     HeapFree(PSDRV_Heap, 0, sz);
481     return TRUE;
482 }
483
484 /*******************************************************************************
485  *  ReadFontMetrics
486  *
487  *  Allocates space for the AFM on the driver heap and reads basic font metrics.
488  *  Returns FALSE for memory allocation failure; sets *p_afm to NULL if AFM file
489  *  is unusable.
490  *
491  */
492 static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm)
493 {
494     AFM     *afm;
495     BOOL    retval, found;
496
497     *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
498     if (afm == NULL)
499         return FALSE;
500
501     retval = ReadWeight(file, buffer, bufsize, afm, &found);
502     if (retval == FALSE || found == FALSE)
503         goto cleanup_afm;
504
505     retval = ReadFloat(file, buffer, bufsize, "ItalicAngle",
506             &(afm->ItalicAngle), &found);
507     if (retval == FALSE || found == FALSE)
508         goto cleanup_afm;
509
510     retval = ReadFixedPitch(file, buffer, bufsize, afm, &found);
511     if (retval == FALSE || found == FALSE)
512         goto cleanup_afm;
513
514     retval = ReadBBox(file, buffer, bufsize, afm, &found);
515     if (retval == FALSE || found == FALSE)
516         goto cleanup_afm;
517
518     retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition",
519             &(afm->UnderlinePosition), &found);
520     if (retval == FALSE || found == FALSE)
521         goto cleanup_afm;
522
523     retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness",
524             &(afm->UnderlineThickness), &found);
525     if (retval == FALSE || found == FALSE)
526         goto cleanup_afm;
527
528     retval = ReadFloat(file, buffer, bufsize, "Ascender",       /* optional */
529             &(afm->Ascender), &found);
530     if (retval == FALSE)
531         goto cleanup_afm;
532
533     retval = ReadFloat(file, buffer, bufsize, "Descender",      /* optional */
534             &(afm->Descender), &found);
535     if (retval == FALSE)
536         goto cleanup_afm;
537
538     afm->WinMetrics.usUnitsPerEm = 1000;
539     afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender);
540     afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender);
541
542     if (afm->WinMetrics.sTypoAscender == 0)
543         afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury);
544
545     if (afm->WinMetrics.sTypoDescender == 0)
546         afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly);
547
548     afm->WinMetrics.sTypoLineGap = 1200 -
549             (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender);
550     if (afm->WinMetrics.sTypoLineGap < 0)
551         afm->WinMetrics.sTypoLineGap = 0;
552
553     return TRUE;
554
555     cleanup_afm:                        /* handle fatal or non-fatal errors */
556         HeapFree(PSDRV_Heap, 0, afm);
557         *p_afm = NULL;
558         return retval;
559 }
560
561 /*******************************************************************************
562  *  ParseC
563  *
564  *  Fatal error:        return FALSE (none defined)
565  *
566  *  Non-fatal error:    leave metrics->C set to INT_MAX
567  *
568  */
569 static BOOL ParseC(LPSTR sz, OLD_AFMMETRICS *metrics)
570 {
571     int     base = 10;
572     long    l;
573     CHAR    *cp, *end_ptr;
574
575     cp = sz + 1;
576
577     if (*cp == 'H')
578     {
579         base = 16;
580         ++cp;
581     }
582
583     errno = 0;
584     l = strtol(cp, &end_ptr, base);
585     if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN)
586     {
587         WARN("Error parsing character code '%s'\n", sz);
588         return TRUE;
589     }
590
591     metrics->C = (INT)l;
592     return TRUE;
593 }
594
595 /*******************************************************************************
596  *  ParseW
597  *
598  *  Fatal error:        return FALSE (none defined)
599  *
600  *  Non-fatal error:    leave metrics->WX set to FLT_MAX
601  *
602  */
603 static BOOL ParseW(LPSTR sz, OLD_AFMMETRICS *metrics)
604 {
605     CHAR    *cp, *end_ptr;
606     BOOL    vector = TRUE;
607     double  d;
608
609     cp = sz + 1;
610
611     if (*cp == '0')
612         ++cp;
613
614     if (*cp == 'X')
615     {
616         vector = FALSE;
617         ++cp;
618     }
619
620     if (!isspace(*cp))
621         goto parse_error;
622
623     errno = 0;
624     d = strtod(cp, &end_ptr);
625     if (end_ptr == cp || errno != 0 ||
626             DoubleToFloat(&(metrics->WX), d) == FALSE)
627         goto parse_error;
628
629     if (vector == FALSE)
630         return TRUE;
631
632     /*  Make sure that Y component of vector is zero */
633
634     d = strtod(cp, &end_ptr);                               /* errno == 0 */
635     if (end_ptr == cp || errno != 0 || d != 0.0)
636     {
637         metrics->WX = FLT_MAX;
638         goto parse_error;
639     }
640
641     return TRUE;
642
643     parse_error:
644         WARN("Error parsing character width '%s'\n", sz);
645         return TRUE;
646 }
647
648 /*******************************************************************************
649  *
650  *  ParseB
651  *
652  *  Fatal error:        return FALSE (none defined)
653  *
654  *  Non-fatal error:    leave metrics->B.ury set to FLT_MAX
655  *
656  */
657 static BOOL ParseB(LPSTR sz, OLD_AFMMETRICS *metrics)
658 {
659     CHAR    *cp, *end_ptr;
660     double  d;
661
662     errno = 0;
663
664     cp = sz + 1;
665     d = strtod(cp, &end_ptr);
666     if (end_ptr == cp || errno != 0 ||
667             DoubleToFloat(&(metrics->B.llx), d) == FALSE)
668         goto parse_error;
669
670     cp = end_ptr;
671     d = strtod(cp, &end_ptr);
672     if (end_ptr == cp || errno != 0 ||
673             DoubleToFloat(&(metrics->B.lly), d) == FALSE)
674         goto parse_error;
675
676     cp = end_ptr;
677     d = strtod(cp, &end_ptr);
678     if (end_ptr == cp || errno != 0 ||
679             DoubleToFloat(&(metrics->B.urx), d) == FALSE)
680         goto parse_error;
681
682     cp = end_ptr;
683     d = strtod(cp, &end_ptr);
684     if (end_ptr == cp || errno != 0 ||
685             DoubleToFloat(&(metrics->B.ury), d) == FALSE)
686         goto parse_error;
687
688     return TRUE;
689
690     parse_error:
691         WARN("Error parsing glyph bounding box '%s'\n", sz);
692         return TRUE;
693 }
694
695 /*******************************************************************************
696  *  ParseN
697  *
698  *  Fatal error:        return FALSE (PSDRV_GlyphName failure)
699  *
700  *  Non-fatal error:    leave metrics-> set to NULL
701  *
702  */
703 static BOOL ParseN(LPSTR sz, OLD_AFMMETRICS *metrics)
704 {
705     CHAR    save, *cp, *end_ptr;
706
707     cp = sz + 1;
708
709     while (isspace(*cp))
710         ++cp;
711
712     end_ptr = cp;
713
714     while (*end_ptr != '\0' && !isspace(*end_ptr))
715         ++end_ptr;
716
717     if (end_ptr == cp)
718     {
719         WARN("Error parsing glyph name '%s'\n", sz);
720         return TRUE;
721     }
722
723     save = *end_ptr;
724     *end_ptr = '\0';
725
726     metrics->N = PSDRV_GlyphName(cp);
727     if (metrics->N == NULL)
728         return FALSE;
729
730     *end_ptr = save;
731     return TRUE;
732 }
733
734 /*******************************************************************************
735  *  ParseCharMetrics
736  *
737  *  Parses the metrics line for a single glyph in an AFM file.  Returns FALSE on
738  *  fatal error; sets *metrics to 'badmetrics' on non-fatal error.
739  *
740  */
741 static const OLD_AFMMETRICS badmetrics =
742 {
743     INT_MAX,                                        /* C */
744     LONG_MAX,                                       /* UV */
745     FLT_MAX,                                        /* WX */
746     NULL,                                           /* N */
747     { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX },         /* B */
748     NULL                                            /* L */
749 };
750
751 static BOOL ParseCharMetrics(LPSTR buffer, INT len, OLD_AFMMETRICS *metrics)
752 {
753     CHAR    *cp = buffer;
754
755     *metrics = badmetrics;
756
757     while (*cp != '\0')
758     {
759         while (isspace(*cp))
760             ++cp;
761
762         switch(*cp)
763         {
764             case 'C':   if (ParseC(cp, metrics) == FALSE)
765                             return FALSE;
766                         break;
767
768             case 'W':   if (ParseW(cp, metrics) == FALSE)
769                             return FALSE;
770                         break;
771
772             case 'N':   if (ParseN(cp, metrics) == FALSE)
773                             return FALSE;
774                         break;
775
776             case 'B':   if (ParseB(cp, metrics) == FALSE)
777                             return FALSE;
778                         break;
779         }
780
781         cp = strchr(cp, ';');
782         if (cp == NULL)
783         {
784             WARN("No terminating semicolon\n");
785             break;
786         }
787
788         ++cp;
789     }
790
791     if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL ||
792             metrics->B.ury == FLT_MAX)
793     {
794         *metrics = badmetrics;
795         return TRUE;
796     }
797
798     return TRUE;
799 }
800
801 /*******************************************************************************
802  *  IsWinANSI
803  *
804  *  Checks whether Unicode value is part of Microsoft code page 1252
805  *
806  */
807 static const LONG ansiChars[21] =
808 {
809     0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
810     0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
811     0x20ac, 0x2122, 0x2219
812 };
813
814 static int cmpUV(const void *a, const void *b)
815 {
816     return (int)(*((const LONG *)a) - *((const LONG *)b));
817 }
818
819 inline static BOOL IsWinANSI(LONG uv)
820 {
821     if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) ||
822             (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) ||
823             (0x2020 <= uv && uv <= 0x2022))
824         return TRUE;
825
826     if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL)
827         return TRUE;
828
829     return FALSE;
830 }
831
832 /*******************************************************************************
833  *  Unicodify
834  *
835  *  Determines Unicode value (UV) for each glyph, based on font encoding.
836  *
837  *      FontSpecific:   Usable encodings (0x20 - 0xff) are mapped into the
838  *                      Unicode private use range U+F020 - U+F0FF.
839  *
840  *      other:          UV determined by glyph name, based on Adobe Glyph List.
841  *
842  *  Also does some font metric calculations that require UVs to be known.
843  *
844  */
845 static int UnicodeGlyphByNameIndex(const void *a, const void *b)
846 {
847     return ((const UNICODEGLYPH *)a)->name->index -
848             ((const UNICODEGLYPH *)b)->name->index;
849 }
850
851 static VOID Unicodify(AFM *afm, OLD_AFMMETRICS *metrics)
852 {
853     INT     i;
854
855     if (strcmp(afm->EncodingScheme, "FontSpecific") == 0)
856     {
857         for (i = 0; i < afm->NumofMetrics; ++i)
858         {
859             if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff)
860             {
861                 metrics[i].UV = ((LONG)(metrics[i].C)) | 0xf000L;
862             }
863             else
864             {
865                 TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz);
866                 metrics[i].UV = -1L;
867             }
868         }
869
870         afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
871         afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
872     }
873     else                                        /* non-FontSpecific encoding */
874     {
875         UNICODEGLYPH    ug, *p_ug;
876
877         PSDRV_IndexGlyphList();         /* for fast searching of glyph names */
878
879         afm->WinMetrics.sAscender = afm->WinMetrics.sDescender = 0;
880
881         for (i = 0; i < afm->NumofMetrics; ++i)
882         {
883             ug.name = metrics[i].N;
884             p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
885                     sizeof(ug), UnicodeGlyphByNameIndex);
886             if (p_ug == NULL)
887             {
888                 TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz);
889                 metrics[i].UV = -1L;
890             }
891             else
892             {
893                 metrics[i].UV = p_ug->UV;
894
895                 if (IsWinANSI(p_ug->UV))
896                 {
897                     SHORT   ury = (SHORT)Round(metrics[i].B.ury);
898                     SHORT   lly = (SHORT)Round(metrics[i].B.lly);
899
900                     if (ury > afm->WinMetrics.sAscender)
901                         afm->WinMetrics.sAscender = ury;
902                     if (lly < afm->WinMetrics.sDescender)
903                         afm->WinMetrics.sDescender = lly;
904                 }
905             }
906         }
907
908         if (afm->WinMetrics.sAscender == 0)
909             afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
910         if (afm->WinMetrics.sDescender == 0)
911             afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
912     }
913
914     afm->WinMetrics.sLineGap =
915             1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender);
916     if (afm->WinMetrics.sLineGap < 0)
917         afm->WinMetrics.sLineGap = 0;
918
919     afm->WinMetrics.usWinAscent = (afm->WinMetrics.sAscender > 0) ?
920             afm->WinMetrics.sAscender : 0;
921     afm->WinMetrics.usWinDescent = (afm->WinMetrics.sDescender < 0) ?
922             -(afm->WinMetrics.sDescender) : 0;
923 }
924
925 /*******************************************************************************
926  *  ReadCharMetrics
927  *
928  *  Reads metrics for all glyphs.  *p_metrics will be NULL on non-fatal error.
929  *
930  */
931 static int OldAFMMetricsByUV(const void *a, const void *b)
932 {
933     return ((const OLD_AFMMETRICS *)a)->UV - ((const OLD_AFMMETRICS *)b)->UV;
934 }
935
936 static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
937         AFMMETRICS **p_metrics)
938 {
939     BOOL            retval, found;
940     OLD_AFMMETRICS  *old_metrics, *encoded_metrics;
941     AFMMETRICS      *metrics;
942     INT             i, len;
943
944     retval = ReadInt(file, buffer, bufsize, "StartCharMetrics",
945             &(afm->NumofMetrics), &found);
946     if (retval == FALSE || found == FALSE)
947     {
948         *p_metrics = NULL;
949         return retval;
950     }
951
952     old_metrics = HeapAlloc(PSDRV_Heap, 0,
953             afm->NumofMetrics * sizeof(*old_metrics));
954     if (old_metrics == NULL)
955         return FALSE;
956
957     for (i = 0; i < afm->NumofMetrics; ++i)
958     {
959         retval = ReadLine(file, buffer, bufsize, &len);
960         if (retval == FALSE)
961             goto cleanup_old_metrics;
962
963         if(len > 0)
964         {
965             retval = ParseCharMetrics(buffer, len, old_metrics + i);
966             if (retval == FALSE || old_metrics[i].C == INT_MAX)
967                 goto cleanup_old_metrics;
968
969             continue;
970         }
971
972         switch (len)
973         {
974             case 0:         --i;
975                             continue;
976
977             case INT_MIN:   WARN("Ignoring long line '%32s...'\n", buffer);
978                             goto cleanup_old_metrics;       /* retval == TRUE */
979
980             case EOF:       WARN("Unexpected EOF\n");
981                             goto cleanup_old_metrics;       /* retval == TRUE */
982         }
983     }
984
985     Unicodify(afm, old_metrics);    /* wait until glyph names have been read */
986
987     qsort(old_metrics, afm->NumofMetrics, sizeof(*old_metrics),
988             OldAFMMetricsByUV);
989
990     for (i = 0; old_metrics[i].UV == -1; ++i);      /* count unencoded glyphs */
991
992     afm->NumofMetrics -= i;
993     encoded_metrics = old_metrics + i;
994
995     afm->Metrics = *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0,
996             afm->NumofMetrics * sizeof(*metrics));
997     if (afm->Metrics == NULL)
998         goto cleanup_old_metrics;                           /* retval == TRUE */
999
1000     for (i = 0; i < afm->NumofMetrics; ++i, ++metrics, ++encoded_metrics)
1001     {
1002         metrics->C = encoded_metrics->C;
1003         metrics->UV = encoded_metrics->UV;
1004         metrics->WX = encoded_metrics->WX;
1005         metrics->N = encoded_metrics->N;
1006     }
1007
1008     HeapFree(PSDRV_Heap, 0, old_metrics);
1009
1010     afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
1011
1012     return TRUE;
1013
1014     cleanup_old_metrics:                /* handle fatal or non-fatal errors */
1015         HeapFree(PSDRV_Heap, 0, old_metrics);
1016         *p_metrics = NULL;
1017         return retval;
1018 }
1019
1020 /*******************************************************************************
1021  *  BuildAFM
1022  *
1023  *  Builds the AFM for a PostScript font and adds it to the driver font list.
1024  *  Returns FALSE only on an unexpected error (memory allocation or I/O error).
1025  *
1026  */
1027 static BOOL BuildAFM(FILE *file)
1028 {
1029     CHAR        buffer[258];            /* allow for <cr>, <lf>, and <nul> */
1030     AFM         *afm;
1031     AFMMETRICS  *metrics;
1032     LPSTR       font_name, full_name, family_name, encoding_scheme;
1033     BOOL        retval, added;
1034
1035     retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm);
1036     if (retval == FALSE || afm == NULL)
1037         return retval;
1038
1039     retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name);
1040     if (retval == FALSE || font_name == NULL)
1041         goto cleanup_afm;
1042
1043     retval = ReadString(file, buffer, sizeof(buffer), "FullName", &full_name);
1044     if (retval == FALSE || full_name == NULL)
1045         goto cleanup_font_name;
1046
1047     retval = ReadString(file, buffer, sizeof(buffer), "FamilyName",
1048             &family_name);
1049     if (retval == FALSE || family_name == NULL)
1050         goto cleanup_full_name;
1051
1052     retval = ReadString(file, buffer, sizeof(buffer), "EncodingScheme",
1053             &encoding_scheme);
1054     if (retval == FALSE || encoding_scheme == NULL)
1055         goto cleanup_family_name;
1056
1057     afm->FontName = font_name;
1058     afm->FullName = full_name;
1059     afm->FamilyName = family_name;
1060     afm->EncodingScheme = encoding_scheme;
1061
1062     retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics);
1063     if (retval == FALSE || metrics == FALSE)
1064         goto cleanup_encoding_scheme;
1065
1066     retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
1067     if (retval == FALSE || added == FALSE)
1068         goto cleanup_encoding_scheme;
1069
1070     return TRUE;
1071
1072     /* clean up after fatal or non-fatal errors */
1073
1074     cleanup_encoding_scheme:
1075         HeapFree(PSDRV_Heap, 0, encoding_scheme);
1076     cleanup_family_name:
1077         HeapFree(PSDRV_Heap, 0, family_name);
1078     cleanup_full_name:
1079         HeapFree(PSDRV_Heap, 0, full_name);
1080     cleanup_font_name:
1081         HeapFree(PSDRV_Heap, 0, font_name);
1082     cleanup_afm:
1083         HeapFree(PSDRV_Heap, 0, afm);
1084
1085     return retval;
1086 }
1087
1088 /*******************************************************************************
1089  *  ReadAFMFile
1090  *
1091  *  Reads font metrics from Type 1 AFM file.  Only returns FALSE for
1092  *  unexpected errors (memory allocation or I/O).
1093  *
1094  */
1095 static BOOL ReadAFMFile(LPCSTR filename)
1096 {
1097     FILE    *f;
1098     BOOL    retval;
1099
1100     TRACE("%s\n", filename);
1101
1102     f = fopen(filename, "r");
1103     if (f == NULL)
1104     {
1105         WARN("%s: %s\n", filename, strerror(errno));
1106         return TRUE;
1107     }
1108
1109     retval = BuildAFM(f);
1110
1111     fclose(f);
1112     return retval;
1113 }
1114
1115 /*******************************************************************************
1116  *  ReadAFMDir
1117  *
1118  *  Reads all Type 1 AFM files in a directory.
1119  *
1120  */
1121 static BOOL ReadAFMDir(LPCSTR dirname)
1122 {
1123     struct dirent   *dent;
1124     DIR             *dir;
1125     CHAR            filename[256];
1126
1127     dir = opendir(dirname);
1128     if (dir == NULL)
1129     {
1130         WARN("%s: %s\n", dirname, strerror(errno));
1131         return TRUE;
1132     }
1133
1134     while ((dent = readdir(dir)) != NULL)
1135     {
1136         CHAR    *file_extension = strchr(dent->d_name, '.');
1137         int     fn_len;
1138
1139         if (file_extension == NULL || strcasecmp(file_extension, ".afm") != 0)
1140             continue;
1141
1142         fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
1143         if (fn_len < 0 || fn_len > sizeof(filename) - 1)
1144         {
1145             WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
1146             continue;
1147         }
1148
1149         if (ReadAFMFile(filename) == FALSE)
1150         {
1151             closedir(dir);
1152             return FALSE;
1153         }
1154     }
1155
1156     closedir(dir);
1157     return TRUE;
1158 }
1159
1160 /*******************************************************************************
1161  *  PSDRV_GetType1Metrics
1162  *
1163  *  Reads font metrics from Type 1 AFM font files in directories listed in the
1164  *  [afmdirs] section of the Wine configuration file.
1165  *
1166  *  If this function fails (returns FALSE), the dirver will fail to initialize
1167  *  and the driver heap will be destroyed, so it's not necessary to HeapFree
1168  *  everything in that event.
1169  *
1170  */
1171 BOOL PSDRV_GetType1Metrics(void)
1172 {
1173     CHAR    name_buf[256], value_buf[256];
1174     INT     i = 0;
1175     HKEY    hkey;
1176     DWORD   type, name_len, value_len;
1177
1178     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1179             "Software\\Wine\\Wine\\Config\\afmdirs",
1180             0, KEY_READ, &hkey) != ERROR_SUCCESS)
1181         return TRUE;
1182
1183     name_len = sizeof(name_buf);
1184     value_len = sizeof(value_buf);
1185
1186     while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf,
1187             &value_len) == ERROR_SUCCESS)
1188     {
1189         value_buf[sizeof(value_buf) - 1] = '\0';
1190
1191         if (ReadAFMDir(value_buf) == FALSE)
1192         {
1193             RegCloseKey(hkey);
1194             return FALSE;
1195         }
1196
1197         /* initialize lengths for new iteration */
1198
1199         name_len = sizeof(name_buf);
1200         value_len = sizeof(value_buf);
1201     }
1202
1203     RegCloseKey(hkey);
1204     return TRUE;
1205 }