appwiz.cpl: Fix compilation on systems that don't support nameless unions.
[wine.git] / dlls / wineps.drv / type1afm.c
blobdd7e0ce3176d36871784825ae7770e6aece28227
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 "config.h"
29 #include "wine/port.h"
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <dirent.h>
36 #include <errno.h>
37 #include <ctype.h>
38 #include <limits.h> /* INT_MIN */
40 #ifdef HAVE_FLOAT_H
41 #include <float.h> /* FLT_MAX */
42 #endif
44 #include "windef.h"
45 #include "winbase.h"
46 #include "winerror.h"
47 #include "winreg.h"
48 #include "psdrv.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
53 /*******************************************************************************
54 * ReadLine
56 * Reads a line from a text file into the buffer and trims trailing whitespace.
57 * Can handle DOS and Unix text files, including weird DOS EOF. Returns FALSE
58 * for unexpected I/O errors; otherwise returns TRUE and sets *p_result to
59 * either the number of characters in the returned string or one of the
60 * following:
62 * 0: Blank (or all whitespace) line. This is just a special case
63 * of the normal behavior.
65 * EOF: End of file has been reached.
67 * INT_MIN: Buffer overflow. Returned string is truncated (duh!) and
68 * trailing whitespace is *not* trimmed. Remaining text in
69 * line is discarded. (I.e. the file pointer is positioned at
70 * the beginning of the next line.)
73 static BOOL ReadLine(FILE *file, CHAR buffer[], INT bufsize, INT *p_result)
75 CHAR *cp;
76 INT i;
78 if (fgets(buffer, bufsize, file) == NULL)
80 if (feof(file) == 0) /* EOF or error? */
82 ERR("%s\n", strerror(errno));
83 return FALSE;
86 *p_result = EOF;
87 return TRUE;
90 cp = strchr(buffer, '\n');
91 if (cp == NULL)
93 i = strlen(buffer);
95 if (i == bufsize - 1) /* max possible; was line truncated? */
98 i = fgetc(file); /* find the newline or EOF */
99 while (i != '\n' && i != EOF);
101 if (i == EOF)
103 if (feof(file) == 0) /* EOF or error? */
105 ERR("%s\n", strerror(errno));
106 return FALSE;
109 WARN("No newline at EOF\n");
112 *p_result = INT_MIN;
113 return TRUE;
115 else /* no newline and not truncated */
117 if (strcmp(buffer, "\x1a") == 0) /* test for DOS EOF */
119 *p_result = EOF;
120 return TRUE;
123 WARN("No newline at EOF\n");
124 cp = buffer + i; /* points to \0 where \n should have been */
130 *cp = '\0'; /* trim trailing whitespace */
131 if (cp == buffer)
132 break; /* don't underflow buffer */
133 --cp;
135 while (isspace(*cp));
137 *p_result = strlen(buffer);
138 return TRUE;
141 /*******************************************************************************
142 * FindLine
144 * Finds a line in the file that begins with the given string. Returns FALSE
145 * for unexpected I/O errors; returns an empty (zero character) string if the
146 * requested line is not found.
148 * NOTE: The file pointer *MUST* be positioned at the beginning of a line when
149 * this function is called. Otherwise, an infinite loop can result.
152 static BOOL FindLine(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key)
154 INT len = strlen(key);
155 LONG start = ftell(file);
159 INT result;
160 BOOL ok;
162 ok = ReadLine(file, buffer, bufsize, &result);
163 if (ok == FALSE)
164 return FALSE;
166 if (result > 0 && strncmp(buffer, key, len) == 0)
167 return TRUE;
169 if (result == EOF)
171 rewind(file);
173 else if (result == INT_MIN)
175 WARN("Line beginning '%32s...' is too long; ignoring\n", buffer);
178 while (ftell(file) != start);
180 WARN("Couldn't find line '%s...' in AFM file\n", key);
181 buffer[0] = '\0';
182 return TRUE;
185 /*******************************************************************************
186 * DoubleToFloat
188 * Utility function to convert double to float while checking for overflow.
189 * Will also catch strtod overflows, since HUGE_VAL > FLT_MAX (at least on
190 * Linux x86/gcc).
193 static inline BOOL DoubleToFloat(float *p_f, double d)
195 if (d > (double)FLT_MAX || d < -(double)FLT_MAX)
196 return FALSE;
198 *p_f = (float)d;
199 return TRUE;
202 /*******************************************************************************
203 * Round
205 * Utility function to add or subtract 0.5 before converting to integer type.
208 static inline float Round(float f)
210 return (f >= 0.0) ? (f + 0.5) : (f - 0.5);
213 /*******************************************************************************
214 * ReadFloat
216 * Finds and parses a line of the form '<key> <value>', where value is a
217 * number. Sets *p_found to FALSE if a corresponding line cannot be found, or
218 * it cannot be parsed; also sets *p_ret to 0.0, so calling functions can just
219 * skip the check of *p_found if the item is not required.
222 static BOOL ReadFloat(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
223 FLOAT *p_ret, BOOL *p_found)
225 CHAR *cp, *end_ptr;
226 double d;
228 if (FindLine(file, buffer, bufsize, key) == FALSE)
229 return FALSE;
231 if (buffer[0] == '\0') /* line not found */
233 *p_found = FALSE;
234 *p_ret = 0.0;
235 return TRUE;
238 cp = buffer + strlen(key); /* first char after key */
239 errno = 0;
240 d = strtod(cp, &end_ptr);
242 if (end_ptr == cp || errno != 0 || DoubleToFloat(p_ret, d) == FALSE)
244 WARN("Error parsing line '%s'\n", buffer);
245 *p_found = FALSE;
246 *p_ret = 0.0;
247 return TRUE;
250 *p_found = TRUE;
251 return TRUE;
254 /*******************************************************************************
255 * ReadInt
257 * See description of ReadFloat.
260 static BOOL ReadInt(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
261 INT *p_ret, BOOL *p_found)
263 BOOL retval;
264 FLOAT f;
266 retval = ReadFloat(file, buffer, bufsize, key, &f, p_found);
267 if (retval == FALSE || *p_found == FALSE)
269 *p_ret = 0;
270 return retval;
273 f = Round(f);
275 if (f > (FLOAT)INT_MAX || f < (FLOAT)INT_MIN)
277 WARN("Error parsing line '%s'\n", buffer);
278 *p_ret = 0;
279 *p_found = FALSE;
280 return TRUE;
283 *p_ret = (INT)f;
284 return TRUE;
287 /*******************************************************************************
288 * ReadString
290 * Returns FALSE on I/O error or memory allocation failure; sets *p_str to NULL
291 * if line cannot be found or can't be parsed.
294 static BOOL ReadString(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
295 LPSTR *p_str)
297 CHAR *cp;
299 if (FindLine(file, buffer, bufsize, key) == FALSE)
300 return FALSE;
302 if (buffer[0] == '\0')
304 *p_str = NULL;
305 return TRUE;
308 cp = buffer + strlen(key); /* first char after key */
309 if (*cp == '\0')
311 *p_str = NULL;
312 return TRUE;
315 while (isspace(*cp)) /* find first non-whitespace char */
316 ++cp;
318 *p_str = HeapAlloc(PSDRV_Heap, 0, strlen(cp) + 1);
319 if (*p_str == NULL)
320 return FALSE;
322 strcpy(*p_str, cp);
323 return TRUE;
326 /*******************************************************************************
327 * ReadBBox
329 * Similar to ReadFloat above.
332 static BOOL ReadBBox(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
333 BOOL *p_found)
335 CHAR *cp, *end_ptr;
336 double d;
338 if (FindLine(file, buffer, bufsize, "FontBBox") == FALSE)
339 return FALSE;
341 if (buffer[0] == '\0')
343 *p_found = FALSE;
344 return TRUE;
347 errno = 0;
349 cp = buffer + sizeof("FontBBox");
350 d = strtod(cp, &end_ptr);
351 if (end_ptr == cp || errno != 0 ||
352 DoubleToFloat(&(afm->FontBBox.llx), d) == FALSE)
353 goto parse_error;
355 cp = end_ptr;
356 d = strtod(cp, &end_ptr);
357 if (end_ptr == cp || errno != 0 ||
358 DoubleToFloat(&(afm->FontBBox.lly), d) == FALSE)
359 goto parse_error;
361 cp = end_ptr;
362 d = strtod(cp, &end_ptr);
363 if (end_ptr == cp || errno != 0
364 || DoubleToFloat(&(afm->FontBBox.urx), d) == FALSE)
365 goto parse_error;
367 cp = end_ptr;
368 d = strtod(cp, &end_ptr);
369 if (end_ptr == cp || errno != 0
370 || DoubleToFloat(&(afm->FontBBox.ury), d) == FALSE)
371 goto parse_error;
373 *p_found = TRUE;
374 return TRUE;
376 parse_error:
377 WARN("Error parsing line '%s'\n", buffer);
378 *p_found = FALSE;
379 return TRUE;
382 /*******************************************************************************
383 * ReadWeight
385 * Finds and parses the 'Weight' line of an AFM file. Only tries to determine
386 * if a font is bold (FW_BOLD) or not (FW_NORMAL) -- ignoring all those cute
387 * little FW_* typedefs in the Win32 doc. AFAICT, this is what the Windows
388 * PostScript driver does.
391 static const struct { LPCSTR keyword; INT weight; } afm_weights[] =
393 { "REGULAR", FW_NORMAL },
394 { "NORMAL", FW_NORMAL },
395 { "ROMAN", FW_NORMAL },
396 { "BOLD", FW_BOLD },
397 { "BOOK", FW_NORMAL },
398 { "MEDIUM", FW_NORMAL },
399 { "LIGHT", FW_NORMAL },
400 { "BLACK", FW_BOLD },
401 { "HEAVY", FW_BOLD },
402 { "DEMI", FW_BOLD },
403 { "ULTRA", FW_BOLD },
404 { "SUPER" , FW_BOLD },
405 { NULL, 0 }
408 static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
409 BOOL *p_found)
411 LPSTR sz;
412 CHAR *cp;
413 INT i;
415 if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE)
416 return FALSE;
418 if (sz == NULL)
420 *p_found = FALSE;
421 return TRUE;
424 for (cp = sz; *cp != '\0'; ++cp)
425 *cp = toupper(*cp);
427 for (i = 0; afm_weights[i].keyword != NULL; ++i)
429 if (strstr(sz, afm_weights[i].keyword) != NULL)
431 afm->Weight = afm_weights[i].weight;
432 *p_found = TRUE;
433 HeapFree(PSDRV_Heap, 0, sz);
434 return TRUE;
438 WARN("Unknown weight '%s'; treating as Roman\n", sz);
440 afm->Weight = FW_NORMAL;
441 *p_found = TRUE;
442 HeapFree(PSDRV_Heap, 0, sz);
443 return TRUE;
446 /*******************************************************************************
447 * ReadFixedPitch
450 static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
451 BOOL *p_found)
453 LPSTR sz;
455 if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE)
456 return FALSE;
458 if (sz == NULL)
460 *p_found = FALSE;
461 return TRUE;
464 if (strcasecmp(sz, "false") == 0)
466 afm->IsFixedPitch = FALSE;
467 *p_found = TRUE;
468 HeapFree(PSDRV_Heap, 0, sz);
469 return TRUE;
472 if (strcasecmp(sz, "true") == 0)
474 afm->IsFixedPitch = TRUE;
475 *p_found = TRUE;
476 HeapFree(PSDRV_Heap, 0, sz);
477 return TRUE;
480 WARN("Can't parse line '%s'\n", buffer);
482 *p_found = FALSE;
483 HeapFree(PSDRV_Heap, 0, sz);
484 return TRUE;
487 /*******************************************************************************
488 * ReadFontMetrics
490 * Allocates space for the AFM on the driver heap and reads basic font metrics.
491 * Returns FALSE for memory allocation failure; sets *p_afm to NULL if AFM file
492 * is unusable.
495 static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm)
497 AFM *afm;
498 BOOL retval, found;
500 *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
501 if (afm == NULL)
502 return FALSE;
504 retval = ReadWeight(file, buffer, bufsize, afm, &found);
505 if (retval == FALSE || found == FALSE)
506 goto cleanup_afm;
508 retval = ReadFloat(file, buffer, bufsize, "ItalicAngle",
509 &(afm->ItalicAngle), &found);
510 if (retval == FALSE || found == FALSE)
511 goto cleanup_afm;
513 retval = ReadFixedPitch(file, buffer, bufsize, afm, &found);
514 if (retval == FALSE || found == FALSE)
515 goto cleanup_afm;
517 retval = ReadBBox(file, buffer, bufsize, afm, &found);
518 if (retval == FALSE || found == FALSE)
519 goto cleanup_afm;
521 retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition",
522 &(afm->UnderlinePosition), &found);
523 if (retval == FALSE || found == FALSE)
524 goto cleanup_afm;
526 retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness",
527 &(afm->UnderlineThickness), &found);
528 if (retval == FALSE || found == FALSE)
529 goto cleanup_afm;
531 retval = ReadFloat(file, buffer, bufsize, "Ascender", /* optional */
532 &(afm->Ascender), &found);
533 if (retval == FALSE)
534 goto cleanup_afm;
536 retval = ReadFloat(file, buffer, bufsize, "Descender", /* optional */
537 &(afm->Descender), &found);
538 if (retval == FALSE)
539 goto cleanup_afm;
541 afm->WinMetrics.usUnitsPerEm = 1000;
542 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender);
543 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender);
545 if (afm->WinMetrics.sTypoAscender == 0)
546 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury);
548 if (afm->WinMetrics.sTypoDescender == 0)
549 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly);
551 afm->WinMetrics.sTypoLineGap = 1200 -
552 (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender);
553 if (afm->WinMetrics.sTypoLineGap < 0)
554 afm->WinMetrics.sTypoLineGap = 0;
556 return TRUE;
558 cleanup_afm: /* handle fatal or non-fatal errors */
559 HeapFree(PSDRV_Heap, 0, afm);
560 *p_afm = NULL;
561 return retval;
564 /*******************************************************************************
565 * ParseC
567 * Fatal error: return FALSE (none defined)
569 * Non-fatal error: leave metrics->C set to INT_MAX
572 static BOOL ParseC(LPSTR sz, OLD_AFMMETRICS *metrics)
574 int base = 10;
575 long l;
576 CHAR *cp, *end_ptr;
578 cp = sz + 1;
580 if (*cp == 'H')
582 base = 16;
583 ++cp;
586 errno = 0;
587 l = strtol(cp, &end_ptr, base);
588 if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN)
590 WARN("Error parsing character code '%s'\n", sz);
591 return TRUE;
594 metrics->C = (INT)l;
595 return TRUE;
598 /*******************************************************************************
599 * ParseW
601 * Fatal error: return FALSE (none defined)
603 * Non-fatal error: leave metrics->WX set to FLT_MAX
606 static BOOL ParseW(LPSTR sz, OLD_AFMMETRICS *metrics)
608 CHAR *cp, *end_ptr;
609 BOOL vector = TRUE;
610 double d;
612 cp = sz + 1;
614 if (*cp == '0')
615 ++cp;
617 if (*cp == 'X')
619 vector = FALSE;
620 ++cp;
623 if (!isspace(*cp))
624 goto parse_error;
626 errno = 0;
627 d = strtod(cp, &end_ptr);
628 if (end_ptr == cp || errno != 0 ||
629 DoubleToFloat(&(metrics->WX), d) == FALSE)
630 goto parse_error;
632 if (vector == FALSE)
633 return TRUE;
635 /* Make sure that Y component of vector is zero */
637 d = strtod(cp, &end_ptr); /* errno == 0 */
638 if (end_ptr == cp || errno != 0 || d != 0.0)
640 metrics->WX = FLT_MAX;
641 goto parse_error;
644 return TRUE;
646 parse_error:
647 WARN("Error parsing character width '%s'\n", sz);
648 return TRUE;
651 /*******************************************************************************
653 * ParseB
655 * Fatal error: return FALSE (none defined)
657 * Non-fatal error: leave metrics->B.ury set to FLT_MAX
660 static BOOL ParseB(LPSTR sz, OLD_AFMMETRICS *metrics)
662 CHAR *cp, *end_ptr;
663 double d;
665 errno = 0;
667 cp = sz + 1;
668 d = strtod(cp, &end_ptr);
669 if (end_ptr == cp || errno != 0 ||
670 DoubleToFloat(&(metrics->B.llx), d) == FALSE)
671 goto parse_error;
673 cp = end_ptr;
674 d = strtod(cp, &end_ptr);
675 if (end_ptr == cp || errno != 0 ||
676 DoubleToFloat(&(metrics->B.lly), d) == FALSE)
677 goto parse_error;
679 cp = end_ptr;
680 d = strtod(cp, &end_ptr);
681 if (end_ptr == cp || errno != 0 ||
682 DoubleToFloat(&(metrics->B.urx), d) == FALSE)
683 goto parse_error;
685 cp = end_ptr;
686 d = strtod(cp, &end_ptr);
687 if (end_ptr == cp || errno != 0 ||
688 DoubleToFloat(&(metrics->B.ury), d) == FALSE)
689 goto parse_error;
691 return TRUE;
693 parse_error:
694 WARN("Error parsing glyph bounding box '%s'\n", sz);
695 return TRUE;
698 /*******************************************************************************
699 * ParseN
701 * Fatal error: return FALSE (PSDRV_GlyphName failure)
703 * Non-fatal error: leave metrics-> set to NULL
706 static BOOL ParseN(LPSTR sz, OLD_AFMMETRICS *metrics)
708 CHAR save, *cp, *end_ptr;
710 cp = sz + 1;
712 while (isspace(*cp))
713 ++cp;
715 end_ptr = cp;
717 while (*end_ptr != '\0' && !isspace(*end_ptr))
718 ++end_ptr;
720 if (end_ptr == cp)
722 WARN("Error parsing glyph name '%s'\n", sz);
723 return TRUE;
726 save = *end_ptr;
727 *end_ptr = '\0';
729 metrics->N = PSDRV_GlyphName(cp);
730 if (metrics->N == NULL)
731 return FALSE;
733 *end_ptr = save;
734 return TRUE;
737 /*******************************************************************************
738 * ParseCharMetrics
740 * Parses the metrics line for a single glyph in an AFM file. Returns FALSE on
741 * fatal error; sets *metrics to 'badmetrics' on non-fatal error.
744 static const OLD_AFMMETRICS badmetrics =
746 INT_MAX, /* C */
747 LONG_MAX, /* UV */
748 FLT_MAX, /* WX */
749 NULL, /* N */
750 { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }, /* B */
751 NULL /* L */
754 static BOOL ParseCharMetrics(LPSTR buffer, INT len, OLD_AFMMETRICS *metrics)
756 CHAR *cp = buffer;
758 *metrics = badmetrics;
760 while (*cp != '\0')
762 while (isspace(*cp))
763 ++cp;
765 switch(*cp)
767 case 'C': if (ParseC(cp, metrics) == FALSE)
768 return FALSE;
769 break;
771 case 'W': if (ParseW(cp, metrics) == FALSE)
772 return FALSE;
773 break;
775 case 'N': if (ParseN(cp, metrics) == FALSE)
776 return FALSE;
777 break;
779 case 'B': if (ParseB(cp, metrics) == FALSE)
780 return FALSE;
781 break;
784 cp = strchr(cp, ';');
785 if (cp == NULL)
787 WARN("No terminating semicolon\n");
788 break;
791 ++cp;
794 if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL ||
795 metrics->B.ury == FLT_MAX)
797 *metrics = badmetrics;
798 return TRUE;
801 return TRUE;
804 /*******************************************************************************
805 * IsWinANSI
807 * Checks whether Unicode value is part of Microsoft code page 1252
810 static const LONG ansiChars[21] =
812 0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
813 0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
814 0x20ac, 0x2122, 0x2219
817 static int cmpUV(const void *a, const void *b)
819 return (int)(*((const LONG *)a) - *((const LONG *)b));
822 static inline BOOL IsWinANSI(LONG uv)
824 if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) ||
825 (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) ||
826 (0x2020 <= uv && uv <= 0x2022))
827 return TRUE;
829 if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL)
830 return TRUE;
832 return FALSE;
835 /*******************************************************************************
836 * Unicodify
838 * Determines Unicode value (UV) for each glyph, based on font encoding.
840 * FontSpecific: Usable encodings (0x20 - 0xff) are mapped into the
841 * Unicode private use range U+F020 - U+F0FF.
843 * other: UV determined by glyph name, based on Adobe Glyph List.
845 * Also does some font metric calculations that require UVs to be known.
848 static int UnicodeGlyphByNameIndex(const void *a, const void *b)
850 return ((const UNICODEGLYPH *)a)->name->index -
851 ((const UNICODEGLYPH *)b)->name->index;
854 static VOID Unicodify(AFM *afm, OLD_AFMMETRICS *metrics)
856 INT i;
858 if (strcmp(afm->EncodingScheme, "FontSpecific") == 0)
860 for (i = 0; i < afm->NumofMetrics; ++i)
862 if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff)
864 metrics[i].UV = metrics[i].C | 0xf000L;
866 else
868 TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz);
869 metrics[i].UV = -1L;
873 afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
874 afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
876 else /* non-FontSpecific encoding */
878 UNICODEGLYPH ug, *p_ug;
880 PSDRV_IndexGlyphList(); /* for fast searching of glyph names */
882 afm->WinMetrics.sAscender = afm->WinMetrics.sDescender = 0;
884 for (i = 0; i < afm->NumofMetrics; ++i)
886 ug.name = metrics[i].N;
887 p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
888 sizeof(ug), UnicodeGlyphByNameIndex);
889 if (p_ug == NULL)
891 TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz);
892 metrics[i].UV = -1L;
894 else
896 metrics[i].UV = p_ug->UV;
898 if (IsWinANSI(p_ug->UV))
900 SHORT ury = (SHORT)Round(metrics[i].B.ury);
901 SHORT lly = (SHORT)Round(metrics[i].B.lly);
903 if (ury > afm->WinMetrics.sAscender)
904 afm->WinMetrics.sAscender = ury;
905 if (lly < afm->WinMetrics.sDescender)
906 afm->WinMetrics.sDescender = lly;
911 if (afm->WinMetrics.sAscender == 0)
912 afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
913 if (afm->WinMetrics.sDescender == 0)
914 afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
917 afm->WinMetrics.sLineGap =
918 1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender);
919 if (afm->WinMetrics.sLineGap < 0)
920 afm->WinMetrics.sLineGap = 0;
922 afm->WinMetrics.usWinAscent = (afm->WinMetrics.sAscender > 0) ?
923 afm->WinMetrics.sAscender : 0;
924 afm->WinMetrics.usWinDescent = (afm->WinMetrics.sDescender < 0) ?
925 -(afm->WinMetrics.sDescender) : 0;
928 /*******************************************************************************
929 * ReadCharMetrics
931 * Reads metrics for all glyphs. *p_metrics will be NULL on non-fatal error.
934 static int OldAFMMetricsByUV(const void *a, const void *b)
936 return ((const OLD_AFMMETRICS *)a)->UV - ((const OLD_AFMMETRICS *)b)->UV;
939 static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
940 AFMMETRICS **p_metrics)
942 BOOL retval, found;
943 OLD_AFMMETRICS *old_metrics, *encoded_metrics;
944 AFMMETRICS *metrics;
945 INT i, len;
947 retval = ReadInt(file, buffer, bufsize, "StartCharMetrics",
948 &(afm->NumofMetrics), &found);
949 if (retval == FALSE || found == FALSE)
951 *p_metrics = NULL;
952 return retval;
955 old_metrics = HeapAlloc(PSDRV_Heap, 0,
956 afm->NumofMetrics * sizeof(*old_metrics));
957 if (old_metrics == NULL)
958 return FALSE;
960 for (i = 0; i < afm->NumofMetrics; ++i)
962 retval = ReadLine(file, buffer, bufsize, &len);
963 if (retval == FALSE)
964 goto cleanup_old_metrics;
966 if(len > 0)
968 retval = ParseCharMetrics(buffer, len, old_metrics + i);
969 if (retval == FALSE || old_metrics[i].C == INT_MAX)
970 goto cleanup_old_metrics;
972 continue;
975 switch (len)
977 case 0: --i;
978 continue;
980 case INT_MIN: WARN("Ignoring long line '%32s...'\n", buffer);
981 goto cleanup_old_metrics; /* retval == TRUE */
983 case EOF: WARN("Unexpected EOF\n");
984 goto cleanup_old_metrics; /* retval == TRUE */
988 Unicodify(afm, old_metrics); /* wait until glyph names have been read */
990 qsort(old_metrics, afm->NumofMetrics, sizeof(*old_metrics),
991 OldAFMMetricsByUV);
993 for (i = 0; old_metrics[i].UV == -1; ++i); /* count unencoded glyphs */
995 afm->NumofMetrics -= i;
996 encoded_metrics = old_metrics + i;
998 afm->Metrics = *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0,
999 afm->NumofMetrics * sizeof(*metrics));
1000 if (afm->Metrics == NULL)
1001 goto cleanup_old_metrics; /* retval == TRUE */
1003 for (i = 0; i < afm->NumofMetrics; ++i, ++metrics, ++encoded_metrics)
1005 metrics->C = encoded_metrics->C;
1006 metrics->UV = encoded_metrics->UV;
1007 metrics->WX = encoded_metrics->WX;
1008 metrics->N = encoded_metrics->N;
1011 HeapFree(PSDRV_Heap, 0, old_metrics);
1013 afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
1015 return TRUE;
1017 cleanup_old_metrics: /* handle fatal or non-fatal errors */
1018 HeapFree(PSDRV_Heap, 0, old_metrics);
1019 *p_metrics = NULL;
1020 return retval;
1023 /*******************************************************************************
1024 * BuildAFM
1026 * Builds the AFM for a PostScript font and adds it to the driver font list.
1027 * Returns FALSE only on an unexpected error (memory allocation or I/O error).
1030 static BOOL BuildAFM(FILE *file)
1032 CHAR buffer[258]; /* allow for <cr>, <lf>, and <nul> */
1033 AFM *afm;
1034 AFMMETRICS *metrics;
1035 LPSTR font_name, full_name, family_name, encoding_scheme;
1036 BOOL retval, added;
1038 retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm);
1039 if (retval == FALSE || afm == NULL)
1040 return retval;
1042 retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name);
1043 if (retval == FALSE || font_name == NULL)
1044 goto cleanup_afm;
1046 retval = ReadString(file, buffer, sizeof(buffer), "FullName", &full_name);
1047 if (retval == FALSE || full_name == NULL)
1048 goto cleanup_font_name;
1050 retval = ReadString(file, buffer, sizeof(buffer), "FamilyName",
1051 &family_name);
1052 if (retval == FALSE || family_name == NULL)
1053 goto cleanup_full_name;
1055 retval = ReadString(file, buffer, sizeof(buffer), "EncodingScheme",
1056 &encoding_scheme);
1057 if (retval == FALSE || encoding_scheme == NULL)
1058 goto cleanup_family_name;
1060 afm->FontName = font_name;
1061 afm->FullName = full_name;
1062 afm->FamilyName = family_name;
1063 afm->EncodingScheme = encoding_scheme;
1065 retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics);
1066 if (retval == FALSE || metrics == FALSE)
1067 goto cleanup_encoding_scheme;
1069 retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
1070 if (retval == FALSE || added == FALSE)
1071 goto cleanup_encoding_scheme;
1073 return TRUE;
1075 /* clean up after fatal or non-fatal errors */
1077 cleanup_encoding_scheme:
1078 HeapFree(PSDRV_Heap, 0, encoding_scheme);
1079 cleanup_family_name:
1080 HeapFree(PSDRV_Heap, 0, family_name);
1081 cleanup_full_name:
1082 HeapFree(PSDRV_Heap, 0, full_name);
1083 cleanup_font_name:
1084 HeapFree(PSDRV_Heap, 0, font_name);
1085 cleanup_afm:
1086 HeapFree(PSDRV_Heap, 0, afm);
1088 return retval;
1091 /*******************************************************************************
1092 * ReadAFMFile
1094 * Reads font metrics from Type 1 AFM file. Only returns FALSE for
1095 * unexpected errors (memory allocation or I/O).
1098 static BOOL ReadAFMFile(LPCSTR filename)
1100 FILE *f;
1101 BOOL retval;
1103 TRACE("%s\n", filename);
1105 f = fopen(filename, "r");
1106 if (f == NULL)
1108 WARN("%s: %s\n", filename, strerror(errno));
1109 return TRUE;
1112 retval = BuildAFM(f);
1114 fclose(f);
1115 return retval;
1118 /*******************************************************************************
1119 * ReadAFMDir
1121 * Reads all Type 1 AFM files in a directory.
1124 static BOOL ReadAFMDir(LPCSTR dirname)
1126 struct dirent *dent;
1127 DIR *dir;
1128 CHAR filename[256];
1130 dir = opendir(dirname);
1131 if (dir == NULL)
1133 WARN("%s: %s\n", dirname, strerror(errno));
1134 return TRUE;
1137 while ((dent = readdir(dir)) != NULL)
1139 CHAR *file_extension = strchr(dent->d_name, '.');
1140 int fn_len;
1142 if (file_extension == NULL || strcasecmp(file_extension, ".afm") != 0)
1143 continue;
1145 fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
1146 if (fn_len < 0 || fn_len > sizeof(filename) - 1)
1148 WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
1149 continue;
1152 if (ReadAFMFile(filename) == FALSE)
1154 closedir(dir);
1155 return FALSE;
1159 closedir(dir);
1160 return TRUE;
1163 /*******************************************************************************
1164 * PSDRV_GetType1Metrics
1166 * Reads font metrics from Type 1 AFM font files in directories listed in the
1167 * [afmdirs] section of the Wine configuration file.
1169 * If this function fails (returns FALSE), the driver will fail to initialize
1170 * and the driver heap will be destroyed, so it's not necessary to HeapFree
1171 * everything in that event.
1174 BOOL PSDRV_GetType1Metrics(void)
1176 static const WCHAR pathW[] = {'A','F','M','P','a','t','h',0};
1177 HKEY hkey;
1178 DWORD len;
1179 LPWSTR valueW;
1180 LPSTR valueA, ptr;
1182 /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
1183 if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) != ERROR_SUCCESS)
1184 return TRUE;
1186 if (RegQueryValueExW( hkey, pathW, NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
1188 len += sizeof(WCHAR);
1189 valueW = HeapAlloc( PSDRV_Heap, 0, len );
1190 if (RegQueryValueExW( hkey, pathW, NULL, NULL, (LPBYTE)valueW, &len ) == ERROR_SUCCESS)
1192 len = WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, NULL, 0, NULL, NULL );
1193 valueA = HeapAlloc( PSDRV_Heap, 0, len );
1194 WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, valueA, len, NULL, NULL );
1195 TRACE( "got AFM font path %s\n", debugstr_a(valueA) );
1196 ptr = valueA;
1197 while (ptr)
1199 LPSTR next = strchr( ptr, ':' );
1200 if (next) *next++ = 0;
1201 if (!ReadAFMDir( ptr ))
1203 RegCloseKey(hkey);
1204 return FALSE;
1206 ptr = next;
1208 HeapFree( PSDRV_Heap, 0, valueA );
1210 HeapFree( PSDRV_Heap, 0, valueW );
1213 RegCloseKey(hkey);
1214 return TRUE;