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