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