Fixed header dependencies to be fully compatible with the Windows
[wine/multimedia.git] / dlls / wineps / type1afm.c
blob119cf17223f4144a9ef38c5b93a836cc7c15a521
1 /*******************************************************************************
2 * Adobe Font Metric (AFM) file parsing finctions for Wine PostScript driver.
3 * See http://partners.adobe.com/asn/developer/pdfs/tn/5004.AFM_Spec.pdf.
5 * Copyright 2001 Ian Pilcher
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.
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.
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
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.
28 #include "config.h"
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 */
39 #ifdef HAVE_FLOAT_H
40 #include <float.h> /* FLT_MAX */
41 #endif
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"
50 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
52 /*******************************************************************************
53 * ReadLine
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:
61 * 0: Blank (or all whitespace) line. This is just a special case
62 * of the normal behavior.
64 * EOF: End of file has been reached.
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.)
72 static BOOL ReadLine(FILE *file, CHAR buffer[], INT bufsize, INT *p_result)
74 CHAR *cp;
75 INT i;
77 if (fgets(buffer, bufsize, file) == NULL)
79 if (feof(file) == 0) /* EOF or error? */
81 ERR("%s\n", strerror(errno));
82 return FALSE;
85 *p_result = EOF;
86 return TRUE;
89 cp = strchr(buffer, '\n');
90 if (cp == NULL)
92 i = strlen(buffer);
94 if (i == bufsize - 1) /* max possible; was line truncated? */
97 i = fgetc(file); /* find the newline or EOF */
98 while (i != '\n' && i != EOF);
100 if (i == EOF)
102 if (feof(file) == 0) /* EOF or error? */
104 ERR("%s\n", strerror(errno));
105 return FALSE;
108 WARN("No newline at EOF\n");
111 *p_result = INT_MIN;
112 return TRUE;
114 else /* no newline and not truncated */
116 if (strcmp(buffer, "\x1a") == 0) /* test for DOS EOF */
118 *p_result = EOF;
119 return TRUE;
122 WARN("No newline at EOF\n");
123 cp = buffer + i; /* points to \0 where \n should have been */
129 *cp = '\0'; /* trim trailing whitespace */
130 if (cp == buffer)
131 break; /* don't underflow buffer */
132 --cp;
134 while (isspace(*cp));
136 *p_result = strlen(buffer);
137 return TRUE;
140 /*******************************************************************************
141 * FindLine
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.
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.
151 static BOOL FindLine(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key)
153 INT len = strlen(key);
154 LONG start = ftell(file);
158 INT result;
159 BOOL ok;
161 ok = ReadLine(file, buffer, bufsize, &result);
162 if (ok == FALSE)
163 return FALSE;
165 if (result > 0 && strncmp(buffer, key, len) == 0)
166 return TRUE;
168 if (result == EOF)
170 rewind(file);
172 else if (result == INT_MIN)
174 WARN("Line beginning '%32s...' is too long; ignoring\n", buffer);
177 while (ftell(file) != start);
179 WARN("Couldn't find line '%s...' in AFM file\n", key);
180 buffer[0] = '\0';
181 return TRUE;
184 /*******************************************************************************
185 * DoubleToFloat
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).
192 inline static BOOL DoubleToFloat(float *p_f, double d)
194 if (d > (double)FLT_MAX || d < -(double)FLT_MAX)
195 return FALSE;
197 *p_f = (float)d;
198 return TRUE;
201 /*******************************************************************************
202 * Round
204 * Utility function to add or subtract 0.5 before converting to integer type.
207 inline static float Round(float f)
209 return (f >= 0.0) ? (f + 0.5) : (f - 0.5);
212 /*******************************************************************************
213 * ReadFloat
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.
221 static BOOL ReadFloat(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
222 FLOAT *p_ret, BOOL *p_found)
224 CHAR *cp, *end_ptr;
225 double d;
227 if (FindLine(file, buffer, bufsize, key) == FALSE)
228 return FALSE;
230 if (buffer[0] == '\0') /* line not found */
232 *p_found = FALSE;
233 *p_ret = 0.0;
234 return TRUE;
237 cp = buffer + strlen(key); /* first char after key */
238 errno = 0;
239 d = strtod(cp, &end_ptr);
241 if (end_ptr == cp || errno != 0 || DoubleToFloat(p_ret, d) == FALSE)
243 WARN("Error parsing line '%s'\n", buffer);
244 *p_found = FALSE;
245 *p_ret = 0.0;
246 return TRUE;
249 *p_found = TRUE;
250 return TRUE;
253 /*******************************************************************************
254 * ReadInt
256 * See description of ReadFloat.
259 static BOOL ReadInt(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
260 INT *p_ret, BOOL *p_found)
262 BOOL retval;
263 FLOAT f;
265 retval = ReadFloat(file, buffer, bufsize, key, &f, p_found);
266 if (retval == FALSE || *p_found == FALSE)
268 *p_ret = 0;
269 return retval;
272 f = Round(f);
274 if (f > (FLOAT)INT_MAX || f < (FLOAT)INT_MIN)
276 WARN("Error parsing line '%s'\n", buffer);
277 *p_ret = 0;
278 *p_found = FALSE;
279 return TRUE;
282 *p_ret = (INT)f;
283 return TRUE;
286 /*******************************************************************************
287 * ReadString
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.
293 static BOOL ReadString(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
294 LPSTR *p_str)
296 CHAR *cp;
298 if (FindLine(file, buffer, bufsize, key) == FALSE)
299 return FALSE;
301 if (buffer[0] == '\0')
303 *p_str = NULL;
304 return TRUE;
307 cp = buffer + strlen(key); /* first char after key */
308 if (*cp == '\0')
310 *p_str = NULL;
311 return TRUE;
314 while (isspace(*cp)) /* find first non-whitespace char */
315 ++cp;
317 *p_str = HeapAlloc(PSDRV_Heap, 0, strlen(cp) + 1);
318 if (*p_str == NULL)
319 return FALSE;
321 strcpy(*p_str, cp);
322 return TRUE;
325 /*******************************************************************************
326 * ReadBBox
328 * Similar to ReadFloat above.
331 static BOOL ReadBBox(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
332 BOOL *p_found)
334 CHAR *cp, *end_ptr;
335 double d;
337 if (FindLine(file, buffer, bufsize, "FontBBox") == FALSE)
338 return FALSE;
340 if (buffer[0] == '\0')
342 *p_found = FALSE;
343 return TRUE;
346 errno = 0;
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;
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;
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;
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;
372 *p_found = TRUE;
373 return TRUE;
375 parse_error:
376 WARN("Error parsing line '%s'\n", buffer);
377 *p_found = FALSE;
378 return TRUE;
381 /*******************************************************************************
382 * ReadWeight
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.
390 static const struct { LPCSTR keyword; INT weight; } afm_weights[] =
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 }
407 static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
408 BOOL *p_found)
410 LPSTR sz;
411 CHAR *cp;
412 INT i;
414 if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE)
415 return FALSE;
417 if (sz == NULL)
419 *p_found = FALSE;
420 return TRUE;
423 for (cp = sz; *cp != '\0'; ++cp)
424 *cp = toupper(*cp);
426 for (i = 0; afm_weights[i].keyword != NULL; ++i)
428 if (strstr(sz, afm_weights[i].keyword) != NULL)
430 afm->Weight = afm_weights[i].weight;
431 *p_found = TRUE;
432 HeapFree(PSDRV_Heap, 0, sz);
433 return TRUE;
437 WARN("Unknown weight '%s'; treating as Roman\n", sz);
439 afm->Weight = FW_NORMAL;
440 *p_found = TRUE;
441 HeapFree(PSDRV_Heap, 0, sz);
442 return TRUE;
445 /*******************************************************************************
446 * ReadFixedPitch
449 static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
450 BOOL *p_found)
452 LPSTR sz;
454 if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE)
455 return FALSE;
457 if (sz == NULL)
459 *p_found = FALSE;
460 return TRUE;
463 if (strcasecmp(sz, "false") == 0)
465 afm->IsFixedPitch = FALSE;
466 *p_found = TRUE;
467 HeapFree(PSDRV_Heap, 0, sz);
468 return TRUE;
471 if (strcasecmp(sz, "true") == 0)
473 afm->IsFixedPitch = TRUE;
474 *p_found = TRUE;
475 HeapFree(PSDRV_Heap, 0, sz);
476 return TRUE;
479 WARN("Can't parse line '%s'\n", buffer);
481 *p_found = FALSE;
482 HeapFree(PSDRV_Heap, 0, sz);
483 return TRUE;
486 /*******************************************************************************
487 * ReadFontMetrics
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.
494 static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm)
496 AFM *afm;
497 BOOL retval, found;
499 *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
500 if (afm == NULL)
501 return FALSE;
503 retval = ReadWeight(file, buffer, bufsize, afm, &found);
504 if (retval == FALSE || found == FALSE)
505 goto cleanup_afm;
507 retval = ReadFloat(file, buffer, bufsize, "ItalicAngle",
508 &(afm->ItalicAngle), &found);
509 if (retval == FALSE || found == FALSE)
510 goto cleanup_afm;
512 retval = ReadFixedPitch(file, buffer, bufsize, afm, &found);
513 if (retval == FALSE || found == FALSE)
514 goto cleanup_afm;
516 retval = ReadBBox(file, buffer, bufsize, afm, &found);
517 if (retval == FALSE || found == FALSE)
518 goto cleanup_afm;
520 retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition",
521 &(afm->UnderlinePosition), &found);
522 if (retval == FALSE || found == FALSE)
523 goto cleanup_afm;
525 retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness",
526 &(afm->UnderlineThickness), &found);
527 if (retval == FALSE || found == FALSE)
528 goto cleanup_afm;
530 retval = ReadFloat(file, buffer, bufsize, "Ascender", /* optional */
531 &(afm->Ascender), &found);
532 if (retval == FALSE)
533 goto cleanup_afm;
535 retval = ReadFloat(file, buffer, bufsize, "Descender", /* optional */
536 &(afm->Descender), &found);
537 if (retval == FALSE)
538 goto cleanup_afm;
540 afm->WinMetrics.usUnitsPerEm = 1000;
541 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender);
542 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender);
544 if (afm->WinMetrics.sTypoAscender == 0)
545 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury);
547 if (afm->WinMetrics.sTypoDescender == 0)
548 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly);
550 afm->WinMetrics.sTypoLineGap = 1200 -
551 (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender);
552 if (afm->WinMetrics.sTypoLineGap < 0)
553 afm->WinMetrics.sTypoLineGap = 0;
555 return TRUE;
557 cleanup_afm: /* handle fatal or non-fatal errors */
558 HeapFree(PSDRV_Heap, 0, afm);
559 *p_afm = NULL;
560 return retval;
563 /*******************************************************************************
564 * ParseC
566 * Fatal error: return FALSE (none defined)
568 * Non-fatal error: leave metrics->C set to INT_MAX
571 static BOOL ParseC(LPSTR sz, OLD_AFMMETRICS *metrics)
573 int base = 10;
574 long l;
575 CHAR *cp, *end_ptr;
577 cp = sz + 1;
579 if (*cp == 'H')
581 base = 16;
582 ++cp;
585 errno = 0;
586 l = strtol(cp, &end_ptr, base);
587 if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN)
589 WARN("Error parsing character code '%s'\n", sz);
590 return TRUE;
593 metrics->C = (INT)l;
594 return TRUE;
597 /*******************************************************************************
598 * ParseW
600 * Fatal error: return FALSE (none defined)
602 * Non-fatal error: leave metrics->WX set to FLT_MAX
605 static BOOL ParseW(LPSTR sz, OLD_AFMMETRICS *metrics)
607 CHAR *cp, *end_ptr;
608 BOOL vector = TRUE;
609 double d;
611 cp = sz + 1;
613 if (*cp == '0')
614 ++cp;
616 if (*cp == 'X')
618 vector = FALSE;
619 ++cp;
622 if (!isspace(*cp))
623 goto parse_error;
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;
631 if (vector == FALSE)
632 return TRUE;
634 /* Make sure that Y component of vector is zero */
636 d = strtod(cp, &end_ptr); /* errno == 0 */
637 if (end_ptr == cp || errno != 0 || d != 0.0)
639 metrics->WX = FLT_MAX;
640 goto parse_error;
643 return TRUE;
645 parse_error:
646 WARN("Error parsing character width '%s'\n", sz);
647 return TRUE;
650 /*******************************************************************************
652 * ParseB
654 * Fatal error: return FALSE (none defined)
656 * Non-fatal error: leave metrics->B.ury set to FLT_MAX
659 static BOOL ParseB(LPSTR sz, OLD_AFMMETRICS *metrics)
661 CHAR *cp, *end_ptr;
662 double d;
664 errno = 0;
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;
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;
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;
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;
690 return TRUE;
692 parse_error:
693 WARN("Error parsing glyph bounding box '%s'\n", sz);
694 return TRUE;
697 /*******************************************************************************
698 * ParseN
700 * Fatal error: return FALSE (PSDRV_GlyphName failure)
702 * Non-fatal error: leave metrics-> set to NULL
705 static BOOL ParseN(LPSTR sz, OLD_AFMMETRICS *metrics)
707 CHAR save, *cp, *end_ptr;
709 cp = sz + 1;
711 while (isspace(*cp))
712 ++cp;
714 end_ptr = cp;
716 while (*end_ptr != '\0' && !isspace(*end_ptr))
717 ++end_ptr;
719 if (end_ptr == cp)
721 WARN("Error parsing glyph name '%s'\n", sz);
722 return TRUE;
725 save = *end_ptr;
726 *end_ptr = '\0';
728 metrics->N = PSDRV_GlyphName(cp);
729 if (metrics->N == NULL)
730 return FALSE;
732 *end_ptr = save;
733 return TRUE;
736 /*******************************************************************************
737 * ParseCharMetrics
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.
743 static const OLD_AFMMETRICS badmetrics =
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 */
753 static BOOL ParseCharMetrics(LPSTR buffer, INT len, OLD_AFMMETRICS *metrics)
755 CHAR *cp = buffer;
757 *metrics = badmetrics;
759 while (*cp != '\0')
761 while (isspace(*cp))
762 ++cp;
764 switch(*cp)
766 case 'C': if (ParseC(cp, metrics) == FALSE)
767 return FALSE;
768 break;
770 case 'W': if (ParseW(cp, metrics) == FALSE)
771 return FALSE;
772 break;
774 case 'N': if (ParseN(cp, metrics) == FALSE)
775 return FALSE;
776 break;
778 case 'B': if (ParseB(cp, metrics) == FALSE)
779 return FALSE;
780 break;
783 cp = strchr(cp, ';');
784 if (cp == NULL)
786 WARN("No terminating semicolon\n");
787 break;
790 ++cp;
793 if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL ||
794 metrics->B.ury == FLT_MAX)
796 *metrics = badmetrics;
797 return TRUE;
800 return TRUE;
803 /*******************************************************************************
804 * IsWinANSI
806 * Checks whether Unicode value is part of Microsoft code page 1252
809 static const LONG ansiChars[21] =
811 0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
812 0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
813 0x20ac, 0x2122, 0x2219
816 static int cmpUV(const void *a, const void *b)
818 return (int)(*((const LONG *)a) - *((const LONG *)b));
821 inline static BOOL IsWinANSI(LONG uv)
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;
828 if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL)
829 return TRUE;
831 return FALSE;
834 /*******************************************************************************
835 * Unicodify
837 * Determines Unicode value (UV) for each glyph, based on font encoding.
839 * FontSpecific: Usable encodings (0x20 - 0xff) are mapped into the
840 * Unicode private use range U+F020 - U+F0FF.
842 * other: UV determined by glyph name, based on Adobe Glyph List.
844 * Also does some font metric calculations that require UVs to be known.
847 static int UnicodeGlyphByNameIndex(const void *a, const void *b)
849 return ((const UNICODEGLYPH *)a)->name->index -
850 ((const UNICODEGLYPH *)b)->name->index;
853 static VOID Unicodify(AFM *afm, OLD_AFMMETRICS *metrics)
855 INT i;
857 if (strcmp(afm->EncodingScheme, "FontSpecific") == 0)
859 for (i = 0; i < afm->NumofMetrics; ++i)
861 if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff)
863 metrics[i].UV = ((LONG)(metrics[i].C)) | 0xf000L;
865 else
867 TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz);
868 metrics[i].UV = -1L;
872 afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
873 afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
875 else /* non-FontSpecific encoding */
877 UNICODEGLYPH ug, *p_ug;
879 PSDRV_IndexGlyphList(); /* for fast searching of glyph names */
881 afm->WinMetrics.sAscender = afm->WinMetrics.sDescender = 0;
883 for (i = 0; i < afm->NumofMetrics; ++i)
885 ug.name = metrics[i].N;
886 p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
887 sizeof(ug), UnicodeGlyphByNameIndex);
888 if (p_ug == NULL)
890 TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz);
891 metrics[i].UV = -1L;
893 else
895 metrics[i].UV = p_ug->UV;
897 if (IsWinANSI(p_ug->UV))
899 SHORT ury = (SHORT)Round(metrics[i].B.ury);
900 SHORT lly = (SHORT)Round(metrics[i].B.lly);
902 if (ury > afm->WinMetrics.sAscender)
903 afm->WinMetrics.sAscender = ury;
904 if (lly < afm->WinMetrics.sDescender)
905 afm->WinMetrics.sDescender = lly;
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);
916 afm->WinMetrics.sLineGap =
917 1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender);
918 if (afm->WinMetrics.sLineGap < 0)
919 afm->WinMetrics.sLineGap = 0;
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;
927 /*******************************************************************************
928 * ReadCharMetrics
930 * Reads metrics for all glyphs. *p_metrics will be NULL on non-fatal error.
933 static int OldAFMMetricsByUV(const void *a, const void *b)
935 return ((const OLD_AFMMETRICS *)a)->UV - ((const OLD_AFMMETRICS *)b)->UV;
938 static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
939 AFMMETRICS **p_metrics)
941 BOOL retval, found;
942 OLD_AFMMETRICS *old_metrics, *encoded_metrics;
943 AFMMETRICS *metrics;
944 INT i, len;
946 retval = ReadInt(file, buffer, bufsize, "StartCharMetrics",
947 &(afm->NumofMetrics), &found);
948 if (retval == FALSE || found == FALSE)
950 *p_metrics = NULL;
951 return retval;
954 old_metrics = HeapAlloc(PSDRV_Heap, 0,
955 afm->NumofMetrics * sizeof(*old_metrics));
956 if (old_metrics == NULL)
957 return FALSE;
959 for (i = 0; i < afm->NumofMetrics; ++i)
961 retval = ReadLine(file, buffer, bufsize, &len);
962 if (retval == FALSE)
963 goto cleanup_old_metrics;
965 if(len > 0)
967 retval = ParseCharMetrics(buffer, len, old_metrics + i);
968 if (retval == FALSE || old_metrics[i].C == INT_MAX)
969 goto cleanup_old_metrics;
971 continue;
974 switch (len)
976 case 0: --i;
977 continue;
979 case INT_MIN: WARN("Ignoring long line '%32s...'\n", buffer);
980 goto cleanup_old_metrics; /* retval == TRUE */
982 case EOF: WARN("Unexpected EOF\n");
983 goto cleanup_old_metrics; /* retval == TRUE */
987 Unicodify(afm, old_metrics); /* wait until glyph names have been read */
989 qsort(old_metrics, afm->NumofMetrics, sizeof(*old_metrics),
990 OldAFMMetricsByUV);
992 for (i = 0; old_metrics[i].UV == -1; ++i); /* count unencoded glyphs */
994 afm->NumofMetrics -= i;
995 encoded_metrics = old_metrics + i;
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 */
1002 for (i = 0; i < afm->NumofMetrics; ++i, ++metrics, ++encoded_metrics)
1004 metrics->C = encoded_metrics->C;
1005 metrics->UV = encoded_metrics->UV;
1006 metrics->WX = encoded_metrics->WX;
1007 metrics->N = encoded_metrics->N;
1010 HeapFree(PSDRV_Heap, 0, old_metrics);
1012 afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
1014 return TRUE;
1016 cleanup_old_metrics: /* handle fatal or non-fatal errors */
1017 HeapFree(PSDRV_Heap, 0, old_metrics);
1018 *p_metrics = NULL;
1019 return retval;
1022 /*******************************************************************************
1023 * BuildAFM
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).
1029 static BOOL BuildAFM(FILE *file)
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;
1037 retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm);
1038 if (retval == FALSE || afm == NULL)
1039 return retval;
1041 retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name);
1042 if (retval == FALSE || font_name == NULL)
1043 goto cleanup_afm;
1045 retval = ReadString(file, buffer, sizeof(buffer), "FullName", &full_name);
1046 if (retval == FALSE || full_name == NULL)
1047 goto cleanup_font_name;
1049 retval = ReadString(file, buffer, sizeof(buffer), "FamilyName",
1050 &family_name);
1051 if (retval == FALSE || family_name == NULL)
1052 goto cleanup_full_name;
1054 retval = ReadString(file, buffer, sizeof(buffer), "EncodingScheme",
1055 &encoding_scheme);
1056 if (retval == FALSE || encoding_scheme == NULL)
1057 goto cleanup_family_name;
1059 afm->FontName = font_name;
1060 afm->FullName = full_name;
1061 afm->FamilyName = family_name;
1062 afm->EncodingScheme = encoding_scheme;
1064 retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics);
1065 if (retval == FALSE || metrics == FALSE)
1066 goto cleanup_encoding_scheme;
1068 retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
1069 if (retval == FALSE || added == FALSE)
1070 goto cleanup_encoding_scheme;
1072 return TRUE;
1074 /* clean up after fatal or non-fatal errors */
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);
1087 return retval;
1090 /*******************************************************************************
1091 * ReadAFMFile
1093 * Reads font metrics from Type 1 AFM file. Only returns FALSE for
1094 * unexpected errors (memory allocation or I/O).
1097 static BOOL ReadAFMFile(LPCSTR filename)
1099 FILE *f;
1100 BOOL retval;
1102 TRACE("%s\n", filename);
1104 f = fopen(filename, "r");
1105 if (f == NULL)
1107 WARN("%s: %s\n", filename, strerror(errno));
1108 return TRUE;
1111 retval = BuildAFM(f);
1113 fclose(f);
1114 return retval;
1117 /*******************************************************************************
1118 * ReadAFMDir
1120 * Reads all Type 1 AFM files in a directory.
1123 static BOOL ReadAFMDir(LPCSTR dirname)
1125 struct dirent *dent;
1126 DIR *dir;
1127 CHAR filename[256];
1129 dir = opendir(dirname);
1130 if (dir == NULL)
1132 WARN("%s: %s\n", dirname, strerror(errno));
1133 return TRUE;
1136 while ((dent = readdir(dir)) != NULL)
1138 CHAR *file_extension = strchr(dent->d_name, '.');
1139 int fn_len;
1141 if (file_extension == NULL || strcasecmp(file_extension, ".afm") != 0)
1142 continue;
1144 fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
1145 if (fn_len < 0 || fn_len > sizeof(filename) - 1)
1147 WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
1148 continue;
1151 if (ReadAFMFile(filename) == FALSE)
1153 closedir(dir);
1154 return FALSE;
1158 closedir(dir);
1159 return TRUE;
1162 /*******************************************************************************
1163 * PSDRV_GetType1Metrics
1165 * Reads font metrics from Type 1 AFM font files in directories listed in the
1166 * [afmdirs] section of the Wine configuration file.
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.
1173 BOOL PSDRV_GetType1Metrics(void)
1175 CHAR name_buf[256], value_buf[256];
1176 INT i = 0;
1177 HKEY hkey;
1178 DWORD type, name_len, value_len;
1180 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1181 "Software\\Wine\\Wine\\Config\\afmdirs",
1182 0, KEY_READ, &hkey) != ERROR_SUCCESS)
1183 return TRUE;
1185 name_len = sizeof(name_buf);
1186 value_len = sizeof(value_buf);
1188 while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf,
1189 &value_len) == ERROR_SUCCESS)
1191 value_buf[sizeof(value_buf) - 1] = '\0';
1193 if (ReadAFMDir(value_buf) == FALSE)
1195 RegCloseKey(hkey);
1196 return FALSE;
1199 /* initialize lengths for new iteration */
1201 name_len = sizeof(name_buf);
1202 value_len = sizeof(value_buf);
1205 RegCloseKey(hkey);
1206 return TRUE;