wined3d: Remove superfluous semicolons.
[wine/wine64.git] / dlls / wineps.drv / type1afm.c
blob85aca4262be8d5a0e898802a4c6436f7cd1d666f
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 #ifdef HAVE_DIRENT_H
36 # include <dirent.h>
37 #endif
38 #include <errno.h>
39 #include <ctype.h>
40 #include <limits.h> /* INT_MIN */
42 #ifdef HAVE_FLOAT_H
43 #include <float.h> /* FLT_MAX */
44 #endif
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"
53 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
55 /*******************************************************************************
56 * ReadLine
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:
64 * 0: Blank (or all whitespace) line. This is just a special case
65 * of the normal behavior.
67 * EOF: End of file has been reached.
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.)
75 static BOOL ReadLine(FILE *file, CHAR buffer[], INT bufsize, INT *p_result)
77 CHAR *cp;
78 INT i;
80 if (fgets(buffer, bufsize, file) == NULL)
82 if (feof(file) == 0) /* EOF or error? */
84 ERR("%s\n", strerror(errno));
85 return FALSE;
88 *p_result = EOF;
89 return TRUE;
92 cp = strchr(buffer, '\n');
93 if (cp == NULL)
95 i = strlen(buffer);
97 if (i == bufsize - 1) /* max possible; was line truncated? */
100 i = fgetc(file); /* find the newline or EOF */
101 while (i != '\n' && i != EOF);
103 if (i == EOF)
105 if (feof(file) == 0) /* EOF or error? */
107 ERR("%s\n", strerror(errno));
108 return FALSE;
111 WARN("No newline at EOF\n");
114 *p_result = INT_MIN;
115 return TRUE;
117 else /* no newline and not truncated */
119 if (strcmp(buffer, "\x1a") == 0) /* test for DOS EOF */
121 *p_result = EOF;
122 return TRUE;
125 WARN("No newline at EOF\n");
126 cp = buffer + i; /* points to \0 where \n should have been */
132 *cp = '\0'; /* trim trailing whitespace */
133 if (cp == buffer)
134 break; /* don't underflow buffer */
135 --cp;
137 while (isspace(*cp));
139 *p_result = strlen(buffer);
140 return TRUE;
143 /*******************************************************************************
144 * FindLine
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.
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.
154 static BOOL FindLine(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key)
156 INT len = strlen(key);
157 LONG start = ftell(file);
161 INT result;
162 BOOL ok;
164 ok = ReadLine(file, buffer, bufsize, &result);
165 if (ok == FALSE)
166 return FALSE;
168 if (result > 0 && strncmp(buffer, key, len) == 0)
169 return TRUE;
171 if (result == EOF)
173 rewind(file);
175 else if (result == INT_MIN)
177 WARN("Line beginning '%32s...' is too long; ignoring\n", buffer);
180 while (ftell(file) != start);
182 WARN("Couldn't find line '%s...' in AFM file\n", key);
183 buffer[0] = '\0';
184 return TRUE;
187 /*******************************************************************************
188 * DoubleToFloat
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).
195 static inline BOOL DoubleToFloat(float *p_f, double d)
197 if (d > (double)FLT_MAX || d < -(double)FLT_MAX)
198 return FALSE;
200 *p_f = (float)d;
201 return TRUE;
204 /*******************************************************************************
205 * Round
207 * Utility function to add or subtract 0.5 before converting to integer type.
210 static inline float Round(float f)
212 return (f >= 0.0) ? (f + 0.5) : (f - 0.5);
215 /*******************************************************************************
216 * ReadFloat
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.
224 static BOOL ReadFloat(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
225 FLOAT *p_ret, BOOL *p_found)
227 CHAR *cp, *end_ptr;
228 double d;
230 if (FindLine(file, buffer, bufsize, key) == FALSE)
231 return FALSE;
233 if (buffer[0] == '\0') /* line not found */
235 *p_found = FALSE;
236 *p_ret = 0.0;
237 return TRUE;
240 cp = buffer + strlen(key); /* first char after key */
241 errno = 0;
242 d = strtod(cp, &end_ptr);
244 if (end_ptr == cp || errno != 0 || DoubleToFloat(p_ret, d) == FALSE)
246 WARN("Error parsing line '%s'\n", buffer);
247 *p_found = FALSE;
248 *p_ret = 0.0;
249 return TRUE;
252 *p_found = TRUE;
253 return TRUE;
256 /*******************************************************************************
257 * ReadInt
259 * See description of ReadFloat.
262 static BOOL ReadInt(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
263 INT *p_ret, BOOL *p_found)
265 BOOL retval;
266 FLOAT f;
268 retval = ReadFloat(file, buffer, bufsize, key, &f, p_found);
269 if (retval == FALSE || *p_found == FALSE)
271 *p_ret = 0;
272 return retval;
275 f = Round(f);
277 if (f > (FLOAT)INT_MAX || f < (FLOAT)INT_MIN)
279 WARN("Error parsing line '%s'\n", buffer);
280 *p_ret = 0;
281 *p_found = FALSE;
282 return TRUE;
285 *p_ret = (INT)f;
286 return TRUE;
289 /*******************************************************************************
290 * ReadString
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.
296 static BOOL ReadString(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
297 LPSTR *p_str)
299 CHAR *cp;
301 if (FindLine(file, buffer, bufsize, key) == FALSE)
302 return FALSE;
304 if (buffer[0] == '\0')
306 *p_str = NULL;
307 return TRUE;
310 cp = buffer + strlen(key); /* first char after key */
311 if (*cp == '\0')
313 *p_str = NULL;
314 return TRUE;
317 while (isspace(*cp)) /* find first non-whitespace char */
318 ++cp;
320 *p_str = HeapAlloc(PSDRV_Heap, 0, strlen(cp) + 1);
321 if (*p_str == NULL)
322 return FALSE;
324 strcpy(*p_str, cp);
325 return TRUE;
328 /*******************************************************************************
329 * ReadBBox
331 * Similar to ReadFloat above.
334 static BOOL ReadBBox(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
335 BOOL *p_found)
337 CHAR *cp, *end_ptr;
338 double d;
340 if (FindLine(file, buffer, bufsize, "FontBBox") == FALSE)
341 return FALSE;
343 if (buffer[0] == '\0')
345 *p_found = FALSE;
346 return TRUE;
349 errno = 0;
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;
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;
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;
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;
375 *p_found = TRUE;
376 return TRUE;
378 parse_error:
379 WARN("Error parsing line '%s'\n", buffer);
380 *p_found = FALSE;
381 return TRUE;
384 /*******************************************************************************
385 * ReadWeight
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.
393 static const struct { LPCSTR keyword; INT weight; } afm_weights[] =
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 }
410 static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
411 BOOL *p_found)
413 LPSTR sz;
414 CHAR *cp;
415 INT i;
417 if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE)
418 return FALSE;
420 if (sz == NULL)
422 *p_found = FALSE;
423 return TRUE;
426 for (cp = sz; *cp != '\0'; ++cp)
427 *cp = toupper(*cp);
429 for (i = 0; afm_weights[i].keyword != NULL; ++i)
431 if (strstr(sz, afm_weights[i].keyword) != NULL)
433 afm->Weight = afm_weights[i].weight;
434 *p_found = TRUE;
435 HeapFree(PSDRV_Heap, 0, sz);
436 return TRUE;
440 WARN("Unknown weight '%s'; treating as Roman\n", sz);
442 afm->Weight = FW_NORMAL;
443 *p_found = TRUE;
444 HeapFree(PSDRV_Heap, 0, sz);
445 return TRUE;
448 /*******************************************************************************
449 * ReadFixedPitch
452 static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
453 BOOL *p_found)
455 LPSTR sz;
457 if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE)
458 return FALSE;
460 if (sz == NULL)
462 *p_found = FALSE;
463 return TRUE;
466 if (strcasecmp(sz, "false") == 0)
468 afm->IsFixedPitch = FALSE;
469 *p_found = TRUE;
470 HeapFree(PSDRV_Heap, 0, sz);
471 return TRUE;
474 if (strcasecmp(sz, "true") == 0)
476 afm->IsFixedPitch = TRUE;
477 *p_found = TRUE;
478 HeapFree(PSDRV_Heap, 0, sz);
479 return TRUE;
482 WARN("Can't parse line '%s'\n", buffer);
484 *p_found = FALSE;
485 HeapFree(PSDRV_Heap, 0, sz);
486 return TRUE;
489 /*******************************************************************************
490 * ReadFontMetrics
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.
497 static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm)
499 AFM *afm;
500 BOOL retval, found;
502 *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
503 if (afm == NULL)
504 return FALSE;
506 retval = ReadWeight(file, buffer, bufsize, afm, &found);
507 if (retval == FALSE || found == FALSE)
508 goto cleanup_afm;
510 retval = ReadFloat(file, buffer, bufsize, "ItalicAngle",
511 &(afm->ItalicAngle), &found);
512 if (retval == FALSE || found == FALSE)
513 goto cleanup_afm;
515 retval = ReadFixedPitch(file, buffer, bufsize, afm, &found);
516 if (retval == FALSE || found == FALSE)
517 goto cleanup_afm;
519 retval = ReadBBox(file, buffer, bufsize, afm, &found);
520 if (retval == FALSE || found == FALSE)
521 goto cleanup_afm;
523 retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition",
524 &(afm->UnderlinePosition), &found);
525 if (retval == FALSE || found == FALSE)
526 goto cleanup_afm;
528 retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness",
529 &(afm->UnderlineThickness), &found);
530 if (retval == FALSE || found == FALSE)
531 goto cleanup_afm;
533 retval = ReadFloat(file, buffer, bufsize, "Ascender", /* optional */
534 &(afm->Ascender), &found);
535 if (retval == FALSE)
536 goto cleanup_afm;
538 retval = ReadFloat(file, buffer, bufsize, "Descender", /* optional */
539 &(afm->Descender), &found);
540 if (retval == FALSE)
541 goto cleanup_afm;
543 afm->WinMetrics.usUnitsPerEm = 1000;
544 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender);
545 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender);
547 if (afm->WinMetrics.sTypoAscender == 0)
548 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury);
550 if (afm->WinMetrics.sTypoDescender == 0)
551 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly);
553 afm->WinMetrics.sTypoLineGap = 1200 -
554 (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender);
555 if (afm->WinMetrics.sTypoLineGap < 0)
556 afm->WinMetrics.sTypoLineGap = 0;
558 return TRUE;
560 cleanup_afm: /* handle fatal or non-fatal errors */
561 HeapFree(PSDRV_Heap, 0, afm);
562 *p_afm = NULL;
563 return retval;
566 /*******************************************************************************
567 * ParseC
569 * Fatal error: return FALSE (none defined)
571 * Non-fatal error: leave metrics->C set to INT_MAX
574 static BOOL ParseC(LPSTR sz, OLD_AFMMETRICS *metrics)
576 int base = 10;
577 long l;
578 CHAR *cp, *end_ptr;
580 cp = sz + 1;
582 if (*cp == 'H')
584 base = 16;
585 ++cp;
588 errno = 0;
589 l = strtol(cp, &end_ptr, base);
590 if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN)
592 WARN("Error parsing character code '%s'\n", sz);
593 return TRUE;
596 metrics->C = (INT)l;
597 return TRUE;
600 /*******************************************************************************
601 * ParseW
603 * Fatal error: return FALSE (none defined)
605 * Non-fatal error: leave metrics->WX set to FLT_MAX
608 static BOOL ParseW(LPSTR sz, OLD_AFMMETRICS *metrics)
610 CHAR *cp, *end_ptr;
611 BOOL vector = TRUE;
612 double d;
614 cp = sz + 1;
616 if (*cp == '0')
617 ++cp;
619 if (*cp == 'X')
621 vector = FALSE;
622 ++cp;
625 if (!isspace(*cp))
626 goto parse_error;
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;
634 if (vector == FALSE)
635 return TRUE;
637 /* Make sure that Y component of vector is zero */
639 d = strtod(cp, &end_ptr); /* errno == 0 */
640 if (end_ptr == cp || errno != 0 || d != 0.0)
642 metrics->WX = FLT_MAX;
643 goto parse_error;
646 return TRUE;
648 parse_error:
649 WARN("Error parsing character width '%s'\n", sz);
650 return TRUE;
653 /*******************************************************************************
655 * ParseB
657 * Fatal error: return FALSE (none defined)
659 * Non-fatal error: leave metrics->B.ury set to FLT_MAX
662 static BOOL ParseB(LPSTR sz, OLD_AFMMETRICS *metrics)
664 CHAR *cp, *end_ptr;
665 double d;
667 errno = 0;
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;
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;
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;
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;
693 return TRUE;
695 parse_error:
696 WARN("Error parsing glyph bounding box '%s'\n", sz);
697 return TRUE;
700 /*******************************************************************************
701 * ParseN
703 * Fatal error: return FALSE (PSDRV_GlyphName failure)
705 * Non-fatal error: leave metrics-> set to NULL
708 static BOOL ParseN(LPSTR sz, OLD_AFMMETRICS *metrics)
710 CHAR save, *cp, *end_ptr;
712 cp = sz + 1;
714 while (isspace(*cp))
715 ++cp;
717 end_ptr = cp;
719 while (*end_ptr != '\0' && !isspace(*end_ptr))
720 ++end_ptr;
722 if (end_ptr == cp)
724 WARN("Error parsing glyph name '%s'\n", sz);
725 return TRUE;
728 save = *end_ptr;
729 *end_ptr = '\0';
731 metrics->N = PSDRV_GlyphName(cp);
732 if (metrics->N == NULL)
733 return FALSE;
735 *end_ptr = save;
736 return TRUE;
739 /*******************************************************************************
740 * ParseCharMetrics
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.
746 static const OLD_AFMMETRICS badmetrics =
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 */
756 static BOOL ParseCharMetrics(LPSTR buffer, INT len, OLD_AFMMETRICS *metrics)
758 CHAR *cp = buffer;
760 *metrics = badmetrics;
762 while (*cp != '\0')
764 while (isspace(*cp))
765 ++cp;
767 switch(*cp)
769 case 'C': if (ParseC(cp, metrics) == FALSE)
770 return FALSE;
771 break;
773 case 'W': if (ParseW(cp, metrics) == FALSE)
774 return FALSE;
775 break;
777 case 'N': if (ParseN(cp, metrics) == FALSE)
778 return FALSE;
779 break;
781 case 'B': if (ParseB(cp, metrics) == FALSE)
782 return FALSE;
783 break;
786 cp = strchr(cp, ';');
787 if (cp == NULL)
789 WARN("No terminating semicolon\n");
790 break;
793 ++cp;
796 if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL ||
797 metrics->B.ury == FLT_MAX)
799 *metrics = badmetrics;
800 return TRUE;
803 return TRUE;
806 /*******************************************************************************
807 * IsWinANSI
809 * Checks whether Unicode value is part of Microsoft code page 1252
812 static const LONG ansiChars[21] =
814 0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
815 0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
816 0x20ac, 0x2122, 0x2219
819 static int cmpUV(const void *a, const void *b)
821 return (int)(*((const LONG *)a) - *((const LONG *)b));
824 static inline BOOL IsWinANSI(LONG uv)
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;
831 if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL)
832 return TRUE;
834 return FALSE;
837 /*******************************************************************************
838 * Unicodify
840 * Determines Unicode value (UV) for each glyph, based on font encoding.
842 * FontSpecific: Usable encodings (0x20 - 0xff) are mapped into the
843 * Unicode private use range U+F020 - U+F0FF.
845 * other: UV determined by glyph name, based on Adobe Glyph List.
847 * Also does some font metric calculations that require UVs to be known.
850 static int UnicodeGlyphByNameIndex(const void *a, const void *b)
852 return ((const UNICODEGLYPH *)a)->name->index -
853 ((const UNICODEGLYPH *)b)->name->index;
856 static VOID Unicodify(AFM *afm, OLD_AFMMETRICS *metrics)
858 INT i;
860 if (strcmp(afm->EncodingScheme, "FontSpecific") == 0)
862 for (i = 0; i < afm->NumofMetrics; ++i)
864 if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff)
866 metrics[i].UV = metrics[i].C | 0xf000L;
868 else
870 TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz);
871 metrics[i].UV = -1L;
875 afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
876 afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
878 else /* non-FontSpecific encoding */
880 UNICODEGLYPH ug, *p_ug;
882 PSDRV_IndexGlyphList(); /* for fast searching of glyph names */
884 afm->WinMetrics.sAscender = afm->WinMetrics.sDescender = 0;
886 for (i = 0; i < afm->NumofMetrics; ++i)
888 ug.name = metrics[i].N;
889 p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
890 sizeof(ug), UnicodeGlyphByNameIndex);
891 if (p_ug == NULL)
893 TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz);
894 metrics[i].UV = -1L;
896 else
898 metrics[i].UV = p_ug->UV;
900 if (IsWinANSI(p_ug->UV))
902 SHORT ury = (SHORT)Round(metrics[i].B.ury);
903 SHORT lly = (SHORT)Round(metrics[i].B.lly);
905 if (ury > afm->WinMetrics.sAscender)
906 afm->WinMetrics.sAscender = ury;
907 if (lly < afm->WinMetrics.sDescender)
908 afm->WinMetrics.sDescender = lly;
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);
919 afm->WinMetrics.sLineGap =
920 1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender);
921 if (afm->WinMetrics.sLineGap < 0)
922 afm->WinMetrics.sLineGap = 0;
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;
930 /*******************************************************************************
931 * ReadCharMetrics
933 * Reads metrics for all glyphs. *p_metrics will be NULL on non-fatal error.
936 static int OldAFMMetricsByUV(const void *a, const void *b)
938 return ((const OLD_AFMMETRICS *)a)->UV - ((const OLD_AFMMETRICS *)b)->UV;
941 static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
942 AFMMETRICS **p_metrics)
944 BOOL retval, found;
945 OLD_AFMMETRICS *old_metrics, *encoded_metrics;
946 AFMMETRICS *metrics;
947 INT i, len;
949 retval = ReadInt(file, buffer, bufsize, "StartCharMetrics",
950 &(afm->NumofMetrics), &found);
951 if (retval == FALSE || found == FALSE)
953 *p_metrics = NULL;
954 return retval;
957 old_metrics = HeapAlloc(PSDRV_Heap, 0,
958 afm->NumofMetrics * sizeof(*old_metrics));
959 if (old_metrics == NULL)
960 return FALSE;
962 for (i = 0; i < afm->NumofMetrics; ++i)
964 retval = ReadLine(file, buffer, bufsize, &len);
965 if (retval == FALSE)
966 goto cleanup_old_metrics;
968 if(len > 0)
970 retval = ParseCharMetrics(buffer, len, old_metrics + i);
971 if (retval == FALSE || old_metrics[i].C == INT_MAX)
972 goto cleanup_old_metrics;
974 continue;
977 switch (len)
979 case 0: --i;
980 continue;
982 case INT_MIN: WARN("Ignoring long line '%32s...'\n", buffer);
983 goto cleanup_old_metrics; /* retval == TRUE */
985 case EOF: WARN("Unexpected EOF\n");
986 goto cleanup_old_metrics; /* retval == TRUE */
990 Unicodify(afm, old_metrics); /* wait until glyph names have been read */
992 qsort(old_metrics, afm->NumofMetrics, sizeof(*old_metrics),
993 OldAFMMetricsByUV);
995 for (i = 0; old_metrics[i].UV == -1; ++i); /* count unencoded glyphs */
997 afm->NumofMetrics -= i;
998 encoded_metrics = old_metrics + i;
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 */
1005 for (i = 0; i < afm->NumofMetrics; ++i, ++metrics, ++encoded_metrics)
1007 metrics->C = encoded_metrics->C;
1008 metrics->UV = encoded_metrics->UV;
1009 metrics->WX = encoded_metrics->WX;
1010 metrics->N = encoded_metrics->N;
1013 HeapFree(PSDRV_Heap, 0, old_metrics);
1015 afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
1017 return TRUE;
1019 cleanup_old_metrics: /* handle fatal or non-fatal errors */
1020 HeapFree(PSDRV_Heap, 0, old_metrics);
1021 *p_metrics = NULL;
1022 return retval;
1025 /*******************************************************************************
1026 * BuildAFM
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).
1032 static BOOL BuildAFM(FILE *file)
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;
1040 retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm);
1041 if (retval == FALSE || afm == NULL)
1042 return retval;
1044 retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name);
1045 if (retval == FALSE || font_name == NULL)
1046 goto cleanup_afm;
1048 retval = ReadString(file, buffer, sizeof(buffer), "FullName", &full_name);
1049 if (retval == FALSE || full_name == NULL)
1050 goto cleanup_font_name;
1052 retval = ReadString(file, buffer, sizeof(buffer), "FamilyName",
1053 &family_name);
1054 if (retval == FALSE || family_name == NULL)
1055 goto cleanup_full_name;
1057 retval = ReadString(file, buffer, sizeof(buffer), "EncodingScheme",
1058 &encoding_scheme);
1059 if (retval == FALSE || encoding_scheme == NULL)
1060 goto cleanup_family_name;
1062 afm->FontName = font_name;
1063 afm->FullName = full_name;
1064 afm->FamilyName = family_name;
1065 afm->EncodingScheme = encoding_scheme;
1067 retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics);
1068 if (retval == FALSE || metrics == FALSE)
1069 goto cleanup_encoding_scheme;
1071 retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
1072 if (retval == FALSE || added == FALSE)
1073 goto cleanup_encoding_scheme;
1075 return TRUE;
1077 /* clean up after fatal or non-fatal errors */
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);
1090 return retval;
1093 /*******************************************************************************
1094 * ReadAFMFile
1096 * Reads font metrics from Type 1 AFM file. Only returns FALSE for
1097 * unexpected errors (memory allocation or I/O).
1100 static BOOL ReadAFMFile(LPCSTR filename)
1102 FILE *f;
1103 BOOL retval;
1105 TRACE("%s\n", filename);
1107 f = fopen(filename, "r");
1108 if (f == NULL)
1110 WARN("%s: %s\n", filename, strerror(errno));
1111 return TRUE;
1114 retval = BuildAFM(f);
1116 fclose(f);
1117 return retval;
1120 /*******************************************************************************
1121 * ReadAFMDir
1123 * Reads all Type 1 AFM files in a directory.
1126 static BOOL ReadAFMDir(LPCSTR dirname)
1128 struct dirent *dent;
1129 DIR *dir;
1130 CHAR filename[256];
1132 dir = opendir(dirname);
1133 if (dir == NULL)
1135 WARN("%s: %s\n", dirname, strerror(errno));
1136 return TRUE;
1139 while ((dent = readdir(dir)) != NULL)
1141 CHAR *file_extension = strchr(dent->d_name, '.');
1142 int fn_len;
1144 if (file_extension == NULL || strcasecmp(file_extension, ".afm") != 0)
1145 continue;
1147 fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
1148 if (fn_len < 0 || fn_len > sizeof(filename) - 1)
1150 WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
1151 continue;
1154 if (ReadAFMFile(filename) == FALSE)
1156 closedir(dir);
1157 return FALSE;
1161 closedir(dir);
1162 return TRUE;
1165 /*******************************************************************************
1166 * PSDRV_GetType1Metrics
1168 * Reads font metrics from Type 1 AFM font files in directories listed in the
1169 * [afmdirs] section of the Wine configuration file.
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.
1176 BOOL PSDRV_GetType1Metrics(void)
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;
1184 /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
1185 if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) != ERROR_SUCCESS)
1186 return TRUE;
1188 if (RegQueryValueExW( hkey, pathW, NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
1190 len += sizeof(WCHAR);
1191 valueW = HeapAlloc( PSDRV_Heap, 0, len );
1192 if (RegQueryValueExW( hkey, pathW, NULL, NULL, (LPBYTE)valueW, &len ) == ERROR_SUCCESS)
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)
1201 LPSTR next = strchr( ptr, ':' );
1202 if (next) *next++ = 0;
1203 if (!ReadAFMDir( ptr ))
1205 RegCloseKey(hkey);
1206 return FALSE;
1208 ptr = next;
1210 HeapFree( PSDRV_Heap, 0, valueA );
1212 HeapFree( PSDRV_Heap, 0, valueW );
1215 RegCloseKey(hkey);
1216 return TRUE;