winemenubuilder: Use CRT allocation functions.
[wine.git] / dlls / wineps.drv / type1afm.c
blobc5ecfb843f02838b3fc8c21f07a594b65fe14cc0
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 <string.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <io.h>
34 #include <ctype.h>
35 #include <limits.h> /* INT_MIN */
36 #include <float.h> /* FLT_MAX */
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winerror.h"
41 #include "winreg.h"
42 #include "winnls.h"
43 #include "winternl.h"
44 #include "psdrv.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
49 /*******************************************************************************
50 * ReadLine
52 * Reads a line from a text file into the buffer and trims trailing whitespace.
53 * Can handle DOS and Unix text files, including weird DOS EOF. Returns FALSE
54 * for unexpected I/O errors; otherwise returns TRUE and sets *p_result to
55 * either the number of characters in the returned string or one of the
56 * following:
58 * 0: Blank (or all whitespace) line. This is just a special case
59 * of the normal behavior.
61 * EOF: End of file has been reached.
63 * INT_MIN: Buffer overflow. Returned string is truncated (duh!) and
64 * trailing whitespace is *not* trimmed. Remaining text in
65 * line is discarded. (I.e. the file pointer is positioned at
66 * the beginning of the next line.)
69 static BOOL ReadLine(FILE *file, CHAR buffer[], INT bufsize, INT *p_result)
71 CHAR *cp;
72 INT i;
74 if (fgets(buffer, bufsize, file) == NULL)
76 if (feof(file) == 0) /* EOF or error? */
78 ERR("%s\n", strerror(errno));
79 return FALSE;
82 *p_result = EOF;
83 return TRUE;
86 cp = strchr(buffer, '\n');
87 if (cp == NULL)
89 i = strlen(buffer);
91 if (i == bufsize - 1) /* max possible; was line truncated? */
94 i = fgetc(file); /* find the newline or EOF */
95 while (i != '\n' && i != EOF);
97 if (i == EOF)
99 if (feof(file) == 0) /* EOF or error? */
101 ERR("%s\n", strerror(errno));
102 return FALSE;
105 WARN("No newline at EOF\n");
108 *p_result = INT_MIN;
109 return TRUE;
111 else /* no newline and not truncated */
113 if (strcmp(buffer, "\x1a") == 0) /* test for DOS EOF */
115 *p_result = EOF;
116 return TRUE;
119 WARN("No newline at EOF\n");
120 cp = buffer + i; /* points to \0 where \n should have been */
126 *cp = '\0'; /* trim trailing whitespace */
127 if (cp == buffer)
128 break; /* don't underflow buffer */
129 --cp;
131 while (isspace(*cp));
133 *p_result = strlen(buffer);
134 return TRUE;
137 /*******************************************************************************
138 * FindLine
140 * Finds a line in the file that begins with the given string. Returns FALSE
141 * for unexpected I/O errors; returns an empty (zero character) string if the
142 * requested line is not found.
144 * NOTE: The file pointer *MUST* be positioned at the beginning of a line when
145 * this function is called. Otherwise, an infinite loop can result.
148 static BOOL FindLine(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key)
150 INT len = strlen(key);
151 LONG start = ftell(file);
155 INT result;
156 BOOL ok;
158 ok = ReadLine(file, buffer, bufsize, &result);
159 if (ok == FALSE)
160 return FALSE;
162 if (result > 0 && strncmp(buffer, key, len) == 0)
163 return TRUE;
165 if (result == EOF)
167 rewind(file);
169 else if (result == INT_MIN)
171 WARN("Line beginning '%32s...' is too long; ignoring\n", buffer);
174 while (ftell(file) != start);
176 WARN("Couldn't find line '%s...' in AFM file\n", key);
177 buffer[0] = '\0';
178 return TRUE;
181 /*******************************************************************************
182 * DoubleToFloat
184 * Utility function to convert double to float while checking for overflow.
185 * Will also catch strtod overflows, since HUGE_VAL > FLT_MAX (at least on
186 * Linux x86/gcc).
189 static inline BOOL DoubleToFloat(float *p_f, double d)
191 if (d > (double)FLT_MAX || d < -(double)FLT_MAX)
192 return FALSE;
194 *p_f = (float)d;
195 return TRUE;
198 /*******************************************************************************
199 * Round
201 * Utility function to add or subtract 0.5 before converting to integer type.
204 static inline float Round(float f)
206 return (f >= 0.0) ? (f + 0.5) : (f - 0.5);
209 /*******************************************************************************
210 * ReadFloat
212 * Finds and parses a line of the form '<key> <value>', where value is a
213 * number. Sets *p_found to FALSE if a corresponding line cannot be found, or
214 * it cannot be parsed; also sets *p_ret to 0.0, so calling functions can just
215 * skip the check of *p_found if the item is not required.
218 static BOOL ReadFloat(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
219 FLOAT *p_ret, BOOL *p_found)
221 CHAR *cp, *end_ptr;
222 double d;
224 if (FindLine(file, buffer, bufsize, key) == FALSE)
225 return FALSE;
227 if (buffer[0] == '\0') /* line not found */
229 *p_found = FALSE;
230 *p_ret = 0.0;
231 return TRUE;
234 cp = buffer + strlen(key); /* first char after key */
235 errno = 0;
236 d = strtod(cp, &end_ptr);
238 if (end_ptr == cp || errno != 0 || DoubleToFloat(p_ret, d) == FALSE)
240 WARN("Error parsing line '%s'\n", buffer);
241 *p_found = FALSE;
242 *p_ret = 0.0;
243 return TRUE;
246 *p_found = TRUE;
247 return TRUE;
250 /*******************************************************************************
251 * ReadInt
253 * See description of ReadFloat.
256 static BOOL ReadInt(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
257 INT *p_ret, BOOL *p_found)
259 BOOL retval;
260 FLOAT f;
262 retval = ReadFloat(file, buffer, bufsize, key, &f, p_found);
263 if (retval == FALSE || *p_found == FALSE)
265 *p_ret = 0;
266 return retval;
269 f = Round(f);
271 if (f > (FLOAT)INT_MAX || f < (FLOAT)INT_MIN)
273 WARN("Error parsing line '%s'\n", buffer);
274 *p_ret = 0;
275 *p_found = FALSE;
276 return TRUE;
279 *p_ret = (INT)f;
280 return TRUE;
283 /*******************************************************************************
284 * ReadString
286 * Returns FALSE on I/O error or memory allocation failure; sets *p_str to NULL
287 * if line cannot be found or can't be parsed.
290 static BOOL ReadString(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
291 LPSTR *p_str)
293 CHAR *cp;
295 if (FindLine(file, buffer, bufsize, key) == FALSE)
296 return FALSE;
298 if (buffer[0] == '\0')
300 *p_str = NULL;
301 return TRUE;
304 cp = buffer + strlen(key); /* first char after key */
305 if (*cp == '\0')
307 *p_str = NULL;
308 return TRUE;
311 while (isspace(*cp)) /* find first non-whitespace char */
312 ++cp;
314 *p_str = HeapAlloc(PSDRV_Heap, 0, strlen(cp) + 1);
315 if (*p_str == NULL)
316 return FALSE;
318 strcpy(*p_str, cp);
319 return TRUE;
322 static BOOL ReadStringW(FILE *file, CHAR *buffer, int size, const char *key, WCHAR **out)
324 char *outA;
325 int len;
327 if (!ReadString(file, buffer, size, key, &outA))
328 return FALSE;
329 if (!outA)
331 *out = NULL;
332 return TRUE;
335 len = MultiByteToWideChar(CP_ACP, 0, outA, -1, NULL, 0);
336 if (len)
337 *out = HeapAlloc(PSDRV_Heap, 0, len * sizeof(WCHAR));
338 if (!len || !*out)
340 HeapFree(PSDRV_Heap, 0, outA);
341 return FALSE;
343 MultiByteToWideChar(CP_ACP, 0, outA, -1, *out, len);
344 HeapFree(PSDRV_Heap, 0, outA);
345 return TRUE;
348 /*******************************************************************************
349 * ReadBBox
351 * Similar to ReadFloat above.
354 static BOOL ReadBBox(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
355 BOOL *p_found)
357 CHAR *cp, *end_ptr;
358 double d;
360 if (FindLine(file, buffer, bufsize, "FontBBox") == FALSE)
361 return FALSE;
363 if (buffer[0] == '\0')
365 *p_found = FALSE;
366 return TRUE;
369 errno = 0;
371 cp = buffer + sizeof("FontBBox");
372 d = strtod(cp, &end_ptr);
373 if (end_ptr == cp || errno != 0 ||
374 DoubleToFloat(&(afm->FontBBox.llx), d) == FALSE)
375 goto parse_error;
377 cp = end_ptr;
378 d = strtod(cp, &end_ptr);
379 if (end_ptr == cp || errno != 0 ||
380 DoubleToFloat(&(afm->FontBBox.lly), d) == FALSE)
381 goto parse_error;
383 cp = end_ptr;
384 d = strtod(cp, &end_ptr);
385 if (end_ptr == cp || errno != 0
386 || DoubleToFloat(&(afm->FontBBox.urx), d) == FALSE)
387 goto parse_error;
389 cp = end_ptr;
390 d = strtod(cp, &end_ptr);
391 if (end_ptr == cp || errno != 0
392 || DoubleToFloat(&(afm->FontBBox.ury), d) == FALSE)
393 goto parse_error;
395 *p_found = TRUE;
396 return TRUE;
398 parse_error:
399 WARN("Error parsing line '%s'\n", buffer);
400 *p_found = FALSE;
401 return TRUE;
404 /*******************************************************************************
405 * ReadWeight
407 * Finds and parses the 'Weight' line of an AFM file. Only tries to determine
408 * if a font is bold (FW_BOLD) or not (FW_NORMAL) -- ignoring all those cute
409 * little FW_* typedefs in the Win32 doc. AFAICT, this is what the Windows
410 * PostScript driver does.
413 static const struct { LPCSTR keyword; INT weight; } afm_weights[] =
415 { "REGULAR", FW_NORMAL },
416 { "NORMAL", FW_NORMAL },
417 { "ROMAN", FW_NORMAL },
418 { "BOLD", FW_BOLD },
419 { "BOOK", FW_NORMAL },
420 { "MEDIUM", FW_NORMAL },
421 { "LIGHT", FW_NORMAL },
422 { "BLACK", FW_BOLD },
423 { "HEAVY", FW_BOLD },
424 { "DEMI", FW_BOLD },
425 { "ULTRA", FW_BOLD },
426 { "SUPER" , FW_BOLD },
427 { NULL, 0 }
430 static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
431 BOOL *p_found)
433 LPSTR sz;
434 CHAR *cp;
435 INT i;
437 if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE)
438 return FALSE;
440 if (sz == NULL)
442 *p_found = FALSE;
443 return TRUE;
446 for (cp = sz; *cp != '\0'; ++cp)
447 *cp = toupper(*cp);
449 for (i = 0; afm_weights[i].keyword != NULL; ++i)
451 if (strstr(sz, afm_weights[i].keyword) != NULL)
453 afm->Weight = afm_weights[i].weight;
454 *p_found = TRUE;
455 HeapFree(PSDRV_Heap, 0, sz);
456 return TRUE;
460 WARN("Unknown weight '%s'; treating as Roman\n", sz);
462 afm->Weight = FW_NORMAL;
463 *p_found = TRUE;
464 HeapFree(PSDRV_Heap, 0, sz);
465 return TRUE;
468 /*******************************************************************************
469 * ReadFixedPitch
472 static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
473 BOOL *p_found)
475 LPSTR sz;
477 if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE)
478 return FALSE;
480 if (sz == NULL)
482 *p_found = FALSE;
483 return TRUE;
486 if (stricmp(sz, "false") == 0)
488 afm->IsFixedPitch = FALSE;
489 *p_found = TRUE;
490 HeapFree(PSDRV_Heap, 0, sz);
491 return TRUE;
494 if (stricmp(sz, "true") == 0)
496 afm->IsFixedPitch = TRUE;
497 *p_found = TRUE;
498 HeapFree(PSDRV_Heap, 0, sz);
499 return TRUE;
502 WARN("Can't parse line '%s'\n", buffer);
504 *p_found = FALSE;
505 HeapFree(PSDRV_Heap, 0, sz);
506 return TRUE;
509 /*******************************************************************************
510 * ReadFontMetrics
512 * Allocates space for the AFM on the driver heap and reads basic font metrics.
513 * Returns FALSE for memory allocation failure; sets *p_afm to NULL if AFM file
514 * is unusable.
517 static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm)
519 AFM *afm;
520 BOOL retval, found;
522 *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
523 if (afm == NULL)
524 return FALSE;
526 retval = ReadWeight(file, buffer, bufsize, afm, &found);
527 if (retval == FALSE || found == FALSE)
528 goto cleanup_afm;
530 retval = ReadFloat(file, buffer, bufsize, "ItalicAngle",
531 &(afm->ItalicAngle), &found);
532 if (retval == FALSE || found == FALSE)
533 goto cleanup_afm;
535 retval = ReadFixedPitch(file, buffer, bufsize, afm, &found);
536 if (retval == FALSE || found == FALSE)
537 goto cleanup_afm;
539 retval = ReadBBox(file, buffer, bufsize, afm, &found);
540 if (retval == FALSE || found == FALSE)
541 goto cleanup_afm;
543 retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition",
544 &(afm->UnderlinePosition), &found);
545 if (retval == FALSE || found == FALSE)
546 goto cleanup_afm;
548 retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness",
549 &(afm->UnderlineThickness), &found);
550 if (retval == FALSE || found == FALSE)
551 goto cleanup_afm;
553 retval = ReadFloat(file, buffer, bufsize, "Ascender", /* optional */
554 &(afm->Ascender), &found);
555 if (retval == FALSE)
556 goto cleanup_afm;
558 retval = ReadFloat(file, buffer, bufsize, "Descender", /* optional */
559 &(afm->Descender), &found);
560 if (retval == FALSE)
561 goto cleanup_afm;
563 afm->WinMetrics.usUnitsPerEm = 1000;
564 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender);
565 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender);
567 if (afm->WinMetrics.sTypoAscender == 0)
568 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury);
570 if (afm->WinMetrics.sTypoDescender == 0)
571 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly);
573 afm->WinMetrics.sTypoLineGap = 1200 -
574 (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender);
575 if (afm->WinMetrics.sTypoLineGap < 0)
576 afm->WinMetrics.sTypoLineGap = 0;
578 return TRUE;
580 cleanup_afm: /* handle fatal or non-fatal errors */
581 HeapFree(PSDRV_Heap, 0, afm);
582 *p_afm = NULL;
583 return retval;
586 /*******************************************************************************
587 * ParseC
589 * Fatal error: return FALSE (none defined)
591 * Non-fatal error: leave metrics->C set to INT_MAX
594 static BOOL ParseC(LPSTR sz, OLD_AFMMETRICS *metrics)
596 int base = 10;
597 long l;
598 CHAR *cp, *end_ptr;
600 cp = sz + 1;
602 if (*cp == 'H')
604 base = 16;
605 ++cp;
608 errno = 0;
609 l = strtol(cp, &end_ptr, base);
610 if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN)
612 WARN("Error parsing character code '%s'\n", sz);
613 return TRUE;
616 metrics->C = (INT)l;
617 return TRUE;
620 /*******************************************************************************
621 * ParseW
623 * Fatal error: return FALSE (none defined)
625 * Non-fatal error: leave metrics->WX set to FLT_MAX
628 static BOOL ParseW(LPSTR sz, OLD_AFMMETRICS *metrics)
630 CHAR *cp, *end_ptr;
631 BOOL vector = TRUE;
632 double d;
634 cp = sz + 1;
636 if (*cp == '0')
637 ++cp;
639 if (*cp == 'X')
641 vector = FALSE;
642 ++cp;
645 if (!isspace(*cp))
646 goto parse_error;
648 errno = 0;
649 d = strtod(cp, &end_ptr);
650 if (end_ptr == cp || errno != 0 ||
651 DoubleToFloat(&(metrics->WX), d) == FALSE)
652 goto parse_error;
654 if (vector == FALSE)
655 return TRUE;
657 /* Make sure that Y component of vector is zero */
659 d = strtod(cp, &end_ptr); /* errno == 0 */
660 if (end_ptr == cp || errno != 0 || d != 0.0)
662 metrics->WX = FLT_MAX;
663 goto parse_error;
666 return TRUE;
668 parse_error:
669 WARN("Error parsing character width '%s'\n", sz);
670 return TRUE;
673 /*******************************************************************************
675 * ParseB
677 * Fatal error: return FALSE (none defined)
679 * Non-fatal error: leave metrics->B.ury set to FLT_MAX
682 static BOOL ParseB(LPSTR sz, OLD_AFMMETRICS *metrics)
684 CHAR *cp, *end_ptr;
685 double d;
687 errno = 0;
689 cp = sz + 1;
690 d = strtod(cp, &end_ptr);
691 if (end_ptr == cp || errno != 0 ||
692 DoubleToFloat(&(metrics->B.llx), d) == FALSE)
693 goto parse_error;
695 cp = end_ptr;
696 d = strtod(cp, &end_ptr);
697 if (end_ptr == cp || errno != 0 ||
698 DoubleToFloat(&(metrics->B.lly), d) == FALSE)
699 goto parse_error;
701 cp = end_ptr;
702 d = strtod(cp, &end_ptr);
703 if (end_ptr == cp || errno != 0 ||
704 DoubleToFloat(&(metrics->B.urx), d) == FALSE)
705 goto parse_error;
707 cp = end_ptr;
708 d = strtod(cp, &end_ptr);
709 if (end_ptr == cp || errno != 0 ||
710 DoubleToFloat(&(metrics->B.ury), d) == FALSE)
711 goto parse_error;
713 return TRUE;
715 parse_error:
716 WARN("Error parsing glyph bounding box '%s'\n", sz);
717 return TRUE;
720 /*******************************************************************************
721 * ParseN
723 * Fatal error: return FALSE (PSDRV_GlyphName failure)
725 * Non-fatal error: leave metrics-> set to NULL
728 static BOOL ParseN(LPSTR sz, OLD_AFMMETRICS *metrics)
730 CHAR save, *cp, *end_ptr;
732 cp = sz + 1;
734 while (isspace(*cp))
735 ++cp;
737 end_ptr = cp;
739 while (*end_ptr != '\0' && !isspace(*end_ptr))
740 ++end_ptr;
742 if (end_ptr == cp)
744 WARN("Error parsing glyph name '%s'\n", sz);
745 return TRUE;
748 save = *end_ptr;
749 *end_ptr = '\0';
751 metrics->N = PSDRV_GlyphName(cp);
752 if (metrics->N == NULL)
753 return FALSE;
755 *end_ptr = save;
756 return TRUE;
759 /*******************************************************************************
760 * ParseCharMetrics
762 * Parses the metrics line for a single glyph in an AFM file. Returns FALSE on
763 * fatal error; sets *metrics to 'badmetrics' on non-fatal error.
766 static const OLD_AFMMETRICS badmetrics =
768 INT_MAX, /* C */
769 INT_MAX, /* UV */
770 FLT_MAX, /* WX */
771 NULL, /* N */
772 { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }, /* B */
773 NULL /* L */
776 static BOOL ParseCharMetrics(LPSTR buffer, INT len, OLD_AFMMETRICS *metrics)
778 CHAR *cp = buffer;
780 *metrics = badmetrics;
782 while (*cp != '\0')
784 while (isspace(*cp))
785 ++cp;
787 switch(*cp)
789 case 'C': if (ParseC(cp, metrics) == FALSE)
790 return FALSE;
791 break;
793 case 'W': if (ParseW(cp, metrics) == FALSE)
794 return FALSE;
795 break;
797 case 'N': if (ParseN(cp, metrics) == FALSE)
798 return FALSE;
799 break;
801 case 'B': if (ParseB(cp, metrics) == FALSE)
802 return FALSE;
803 break;
806 cp = strchr(cp, ';');
807 if (cp == NULL)
809 WARN("No terminating semicolon\n");
810 break;
813 ++cp;
816 if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL ||
817 metrics->B.ury == FLT_MAX)
819 *metrics = badmetrics;
820 return TRUE;
823 return TRUE;
826 /*******************************************************************************
827 * IsWinANSI
829 * Checks whether Unicode value is part of Microsoft code page 1252
832 static const LONG ansiChars[21] =
834 0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
835 0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
836 0x20ac, 0x2122, 0x2219
839 static int __cdecl cmpUV(const void *a, const void *b)
841 return (int)(*((const LONG *)a) - *((const LONG *)b));
844 static inline BOOL IsWinANSI(LONG uv)
846 if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) ||
847 (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) ||
848 (0x2020 <= uv && uv <= 0x2022))
849 return TRUE;
851 if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL)
852 return TRUE;
854 return FALSE;
857 /*******************************************************************************
858 * Unicodify
860 * Determines Unicode value (UV) for each glyph, based on font encoding.
862 * FontSpecific: Usable encodings (0x20 - 0xff) are mapped into the
863 * Unicode private use range U+F020 - U+F0FF.
865 * other: UV determined by glyph name, based on Adobe Glyph List.
867 * Also does some font metric calculations that require UVs to be known.
870 static int __cdecl UnicodeGlyphByNameIndex(const void *a, const void *b)
872 return ((const UNICODEGLYPH *)a)->name->index -
873 ((const UNICODEGLYPH *)b)->name->index;
876 static VOID Unicodify(AFM *afm, OLD_AFMMETRICS *metrics)
878 INT i;
880 if (wcscmp(afm->EncodingScheme, L"FontSpecific") == 0)
882 for (i = 0; i < afm->NumofMetrics; ++i)
884 if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff)
886 metrics[i].UV = metrics[i].C | 0xf000L;
888 else
890 TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz);
891 metrics[i].UV = -1L;
895 afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
896 afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
898 else /* non-FontSpecific encoding */
900 UNICODEGLYPH ug, *p_ug;
902 PSDRV_IndexGlyphList(); /* for fast searching of glyph names */
904 afm->WinMetrics.sAscender = afm->WinMetrics.sDescender = 0;
906 for (i = 0; i < afm->NumofMetrics; ++i)
908 ug.name = metrics[i].N;
909 p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
910 sizeof(ug), UnicodeGlyphByNameIndex);
911 if (p_ug == NULL)
913 TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz);
914 metrics[i].UV = -1L;
916 else
918 metrics[i].UV = p_ug->UV;
920 if (IsWinANSI(p_ug->UV))
922 SHORT ury = (SHORT)Round(metrics[i].B.ury);
923 SHORT lly = (SHORT)Round(metrics[i].B.lly);
925 if (ury > afm->WinMetrics.sAscender)
926 afm->WinMetrics.sAscender = ury;
927 if (lly < afm->WinMetrics.sDescender)
928 afm->WinMetrics.sDescender = lly;
933 if (afm->WinMetrics.sAscender == 0)
934 afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
935 if (afm->WinMetrics.sDescender == 0)
936 afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
939 afm->WinMetrics.sLineGap =
940 1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender);
941 if (afm->WinMetrics.sLineGap < 0)
942 afm->WinMetrics.sLineGap = 0;
944 afm->WinMetrics.usWinAscent = (afm->WinMetrics.sAscender > 0) ?
945 afm->WinMetrics.sAscender : 0;
946 afm->WinMetrics.usWinDescent = (afm->WinMetrics.sDescender < 0) ?
947 -(afm->WinMetrics.sDescender) : 0;
950 /*******************************************************************************
951 * ReadCharMetrics
953 * Reads metrics for all glyphs. *p_metrics will be NULL on non-fatal error.
956 static int __cdecl OldAFMMetricsByUV(const void *a, const void *b)
958 return ((const OLD_AFMMETRICS *)a)->UV - ((const OLD_AFMMETRICS *)b)->UV;
961 static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
962 AFMMETRICS **p_metrics)
964 BOOL retval, found;
965 OLD_AFMMETRICS *old_metrics, *encoded_metrics;
966 AFMMETRICS *metrics;
967 INT i, len;
969 retval = ReadInt(file, buffer, bufsize, "StartCharMetrics",
970 &(afm->NumofMetrics), &found);
971 if (retval == FALSE || found == FALSE)
973 *p_metrics = NULL;
974 return retval;
977 old_metrics = HeapAlloc(PSDRV_Heap, 0,
978 afm->NumofMetrics * sizeof(*old_metrics));
979 if (old_metrics == NULL)
980 return FALSE;
982 for (i = 0; i < afm->NumofMetrics; ++i)
984 retval = ReadLine(file, buffer, bufsize, &len);
985 if (retval == FALSE)
986 goto cleanup_old_metrics;
988 if(len > 0)
990 retval = ParseCharMetrics(buffer, len, old_metrics + i);
991 if (retval == FALSE || old_metrics[i].C == INT_MAX)
992 goto cleanup_old_metrics;
994 continue;
997 switch (len)
999 case 0: --i;
1000 continue;
1002 case INT_MIN: WARN("Ignoring long line '%32s...'\n", buffer);
1003 goto cleanup_old_metrics; /* retval == TRUE */
1005 case EOF: WARN("Unexpected EOF\n");
1006 goto cleanup_old_metrics; /* retval == TRUE */
1010 Unicodify(afm, old_metrics); /* wait until glyph names have been read */
1012 qsort(old_metrics, afm->NumofMetrics, sizeof(*old_metrics),
1013 OldAFMMetricsByUV);
1015 for (i = 0; old_metrics[i].UV == -1; ++i); /* count unencoded glyphs */
1017 afm->NumofMetrics -= i;
1018 encoded_metrics = old_metrics + i;
1020 afm->Metrics = *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0,
1021 afm->NumofMetrics * sizeof(*metrics));
1022 if (afm->Metrics == NULL)
1023 goto cleanup_old_metrics; /* retval == TRUE */
1025 for (i = 0; i < afm->NumofMetrics; ++i, ++metrics, ++encoded_metrics)
1027 metrics->C = encoded_metrics->C;
1028 metrics->UV = encoded_metrics->UV;
1029 metrics->WX = encoded_metrics->WX;
1030 metrics->N = encoded_metrics->N;
1033 HeapFree(PSDRV_Heap, 0, old_metrics);
1035 afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
1037 return TRUE;
1039 cleanup_old_metrics: /* handle fatal or non-fatal errors */
1040 HeapFree(PSDRV_Heap, 0, old_metrics);
1041 *p_metrics = NULL;
1042 return retval;
1045 /*******************************************************************************
1046 * BuildAFM
1048 * Builds the AFM for a PostScript font and adds it to the driver font list.
1049 * Returns FALSE only on an unexpected error (memory allocation or I/O error).
1052 static BOOL BuildAFM(FILE *file)
1054 CHAR buffer[258]; /* allow for <cr>, <lf>, and <nul> */
1055 AFM *afm;
1056 AFMMETRICS *metrics;
1057 WCHAR *full_name, *family_name, *encoding_scheme;
1058 char *font_name;
1059 BOOL retval, added;
1061 retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm);
1062 if (retval == FALSE || afm == NULL)
1063 return retval;
1065 retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name);
1066 if (retval == FALSE || font_name == NULL)
1067 goto cleanup_afm;
1069 retval = ReadStringW(file, buffer, sizeof(buffer), "FullName", &full_name);
1070 if (retval == FALSE || full_name == NULL)
1071 goto cleanup_font_name;
1073 retval = ReadStringW(file, buffer, sizeof(buffer), "FamilyName",
1074 &family_name);
1075 if (retval == FALSE || family_name == NULL)
1076 goto cleanup_full_name;
1078 retval = ReadStringW(file, buffer, sizeof(buffer), "EncodingScheme",
1079 &encoding_scheme);
1080 if (retval == FALSE || encoding_scheme == NULL)
1081 goto cleanup_family_name;
1083 afm->FontName = font_name;
1084 afm->FullName = full_name;
1085 afm->FamilyName = family_name;
1086 afm->EncodingScheme = encoding_scheme;
1088 retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics);
1089 if (retval == FALSE || metrics == FALSE)
1090 goto cleanup_encoding_scheme;
1092 retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
1093 if (retval == FALSE || added == FALSE)
1094 goto cleanup_encoding_scheme;
1096 return TRUE;
1098 /* clean up after fatal or non-fatal errors */
1100 cleanup_encoding_scheme:
1101 HeapFree(PSDRV_Heap, 0, encoding_scheme);
1102 cleanup_family_name:
1103 HeapFree(PSDRV_Heap, 0, family_name);
1104 cleanup_full_name:
1105 HeapFree(PSDRV_Heap, 0, full_name);
1106 cleanup_font_name:
1107 HeapFree(PSDRV_Heap, 0, font_name);
1108 cleanup_afm:
1109 HeapFree(PSDRV_Heap, 0, afm);
1111 return retval;
1114 /*******************************************************************************
1115 * ReadAFMFile
1117 * Reads font metrics from Type 1 AFM file. Only returns FALSE for
1118 * unexpected errors (memory allocation or I/O).
1121 static BOOL ReadAFMFile(LPCWSTR filename)
1123 FILE *f;
1124 BOOL retval;
1126 TRACE("%s\n", debugstr_w(filename));
1128 f = _wfopen(filename, L"r");
1129 if (f == NULL)
1131 WARN("%s: %s\n", debugstr_w(filename), strerror(errno));
1132 return TRUE;
1135 retval = BuildAFM(f);
1137 fclose(f);
1138 return retval;
1141 /*******************************************************************************
1142 * ReadAFMDir
1144 * Reads all Type 1 AFM files in a directory.
1147 static BOOL ReadAFMDir(LPCSTR dirname)
1149 WCHAR *path = wine_get_dos_file_name( dirname );
1150 struct _wfinddata_t data;
1151 intptr_t handle;
1152 WCHAR *p, *filename;
1153 BOOL ret = TRUE;
1155 if (!path) return TRUE;
1156 if (!(filename = HeapAlloc( GetProcessHeap(), 0, lstrlenW(path) + 256 )))
1158 HeapFree( GetProcessHeap(), 0, path );
1159 return FALSE;
1161 lstrcpyW( filename, path );
1162 HeapFree( GetProcessHeap(), 0, path );
1163 p = filename + lstrlenW(filename);
1164 *p++ = '\\';
1165 lstrcpyW( p, L"*" );
1167 handle = _wfindfirst( filename, &data );
1168 if (handle != -1)
1172 WCHAR *ext = wcschr( data.name, '.' );
1173 if (!ext || wcsicmp(ext, L".afm")) continue;
1174 lstrcpyW( p, data.name );
1175 if (!(ret = ReadAFMFile( filename ))) break;
1176 } while (!_wfindnext( handle, &data ));
1178 _findclose( handle );
1180 HeapFree( GetProcessHeap(), 0, filename );
1181 return ret;
1184 /*******************************************************************************
1185 * PSDRV_GetType1Metrics
1187 * Reads font metrics from Type 1 AFM font files in directories listed in the
1188 * HKEY_CURRENT_USER\\Software\\Wine\\Fonts\\AFMPath registry string.
1190 * If this function fails (returns FALSE), the driver will fail to initialize
1191 * and the driver heap will be destroyed, so it's not necessary to HeapFree
1192 * everything in that event.
1195 BOOL PSDRV_GetType1Metrics(void)
1197 HKEY hkey;
1198 DWORD len;
1199 LPWSTR valueW;
1200 LPSTR valueA, ptr;
1202 /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
1203 if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) != ERROR_SUCCESS)
1204 return TRUE;
1206 if (RegQueryValueExW( hkey, L"AFMPath", NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
1208 len += sizeof(WCHAR);
1209 valueW = HeapAlloc( PSDRV_Heap, 0, len );
1210 if (RegQueryValueExW( hkey, L"AFMPath", NULL, NULL, (BYTE *)valueW, &len ) == ERROR_SUCCESS)
1212 len = WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, NULL, 0, NULL, NULL );
1213 valueA = HeapAlloc( PSDRV_Heap, 0, len );
1214 WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, valueA, len, NULL, NULL );
1215 TRACE( "got AFM font path %s\n", debugstr_a(valueA) );
1216 ptr = valueA;
1217 while (ptr)
1219 LPSTR next = strchr( ptr, ':' );
1220 if (next) *next++ = 0;
1221 if (!ReadAFMDir( ptr ))
1223 RegCloseKey(hkey);
1224 return FALSE;
1226 ptr = next;
1228 HeapFree( PSDRV_Heap, 0, valueA );
1230 HeapFree( PSDRV_Heap, 0, valueW );
1233 RegCloseKey(hkey);
1234 return TRUE;