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