Merged the NAS driver written by Nicolas
[wine/multimedia.git] / dlls / wineps / type1afm.c
blob803bfdf8172ae5f7beadeae1a6279296f8e85be7
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 <stdio.h>
33 #include <dirent.h>
34 #include <errno.h>
35 #include <ctype.h>
36 #include <limits.h> /* INT_MIN */
38 #ifdef HAVE_FLOAT_H
39 #include <float.h> /* FLT_MAX */
40 #endif
42 #include "winnt.h"
43 #include "winerror.h"
44 #include "winreg.h"
45 #include "psdrv.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
50 /*******************************************************************************
51 * ReadLine
53 * Reads a line from a text file into the buffer and trims trailing whitespace.
54 * Can handle DOS and Unix text files, including weird DOS EOF. Returns FALSE
55 * for unexpected I/O errors; otherwise returns TRUE and sets *p_result to
56 * either the number of characters in the returned string or one of the
57 * following:
59 * 0: Blank (or all whitespace) line. This is just a special case
60 * of the normal behavior.
62 * EOF: End of file has been reached.
64 * INT_MIN: Buffer overflow. Returned string is truncated (duh!) and
65 * trailing whitespace is *not* trimmed. Remaining text in
66 * line is discarded. (I.e. the file pointer is positioned at
67 * the beginning of the next line.)
70 static BOOL ReadLine(FILE *file, CHAR buffer[], INT bufsize, INT *p_result)
72 CHAR *cp;
73 INT i;
75 if (fgets(buffer, bufsize, file) == NULL)
77 if (feof(file) == 0) /* EOF or error? */
79 ERR("%s\n", strerror(errno));
80 return FALSE;
83 *p_result = EOF;
84 return TRUE;
87 cp = strchr(buffer, '\n');
88 if (cp == NULL)
90 i = strlen(buffer);
92 if (i == bufsize - 1) /* max possible; was line truncated? */
95 i = fgetc(file); /* find the newline or EOF */
96 while (i != '\n' && i != EOF);
98 if (i == EOF)
100 if (feof(file) == 0) /* EOF or error? */
102 ERR("%s\n", strerror(errno));
103 return FALSE;
106 WARN("No newline at EOF\n");
109 *p_result = INT_MIN;
110 return TRUE;
112 else /* no newline and not truncated */
114 if (strcmp(buffer, "\x1a") == 0) /* test for DOS EOF */
116 *p_result = EOF;
117 return TRUE;
120 WARN("No newline at EOF\n");
121 cp = buffer + i; /* points to \0 where \n should have been */
127 *cp = '\0'; /* trim trailing whitespace */
128 if (cp == buffer)
129 break; /* don't underflow buffer */
130 --cp;
132 while (isspace(*cp));
134 *p_result = strlen(buffer);
135 return TRUE;
138 /*******************************************************************************
139 * FindLine
141 * Finds a line in the file that begins with the given string. Returns FALSE
142 * for unexpected I/O errors; returns an empty (zero character) string if the
143 * requested line is not found.
145 * NOTE: The file pointer *MUST* be positioned at the beginning of a line when
146 * this function is called. Otherwise, an infinite loop can result.
149 static BOOL FindLine(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key)
151 INT len = strlen(key);
152 LONG start = ftell(file);
156 INT result;
157 BOOL ok;
159 ok = ReadLine(file, buffer, bufsize, &result);
160 if (ok == FALSE)
161 return FALSE;
163 if (result > 0 && strncmp(buffer, key, len) == 0)
164 return TRUE;
166 if (result == EOF)
168 rewind(file);
170 else if (result == INT_MIN)
172 WARN("Line beginning '%32s...' is too long; ignoring\n", buffer);
175 while (ftell(file) != start);
177 WARN("Couldn't find line '%s...' in AFM file\n", key);
178 buffer[0] = '\0';
179 return TRUE;
182 /*******************************************************************************
183 * DoubleToFloat
185 * Utility function to convert double to float while checking for overflow.
186 * Will also catch strtod overflows, since HUGE_VAL > FLT_MAX (at least on
187 * Linux x86/gcc).
190 inline static BOOL DoubleToFloat(float *p_f, double d)
192 if (d > (double)FLT_MAX || d < -(double)FLT_MAX)
193 return FALSE;
195 *p_f = (float)d;
196 return TRUE;
199 /*******************************************************************************
200 * Round
202 * Utility function to add or subtract 0.5 before converting to integer type.
205 inline static float Round(float f)
207 return (f >= 0.0) ? (f + 0.5) : (f - 0.5);
210 /*******************************************************************************
211 * ReadFloat
213 * Finds and parses a line of the form '<key> <value>', where value is a
214 * number. Sets *p_found to FALSE if a corresponding line cannot be found, or
215 * it cannot be parsed; also sets *p_ret to 0.0, so calling functions can just
216 * skip the check of *p_found if the item is not required.
219 static BOOL ReadFloat(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
220 FLOAT *p_ret, BOOL *p_found)
222 CHAR *cp, *end_ptr;
223 double d;
225 if (FindLine(file, buffer, bufsize, key) == FALSE)
226 return FALSE;
228 if (buffer[0] == '\0') /* line not found */
230 *p_found = FALSE;
231 *p_ret = 0.0;
232 return TRUE;
235 cp = buffer + strlen(key); /* first char after key */
236 errno = 0;
237 d = strtod(cp, &end_ptr);
239 if (end_ptr == cp || errno != 0 || DoubleToFloat(p_ret, d) == FALSE)
241 WARN("Error parsing line '%s'\n", buffer);
242 *p_found = FALSE;
243 *p_ret = 0.0;
244 return TRUE;
247 *p_found = TRUE;
248 return TRUE;
251 /*******************************************************************************
252 * ReadInt
254 * See description of ReadFloat.
257 static BOOL ReadInt(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
258 INT *p_ret, BOOL *p_found)
260 BOOL retval;
261 FLOAT f;
263 retval = ReadFloat(file, buffer, bufsize, key, &f, p_found);
264 if (retval == FALSE || *p_found == FALSE)
266 *p_ret = 0;
267 return retval;
270 f = Round(f);
272 if (f > (FLOAT)INT_MAX || f < (FLOAT)INT_MIN)
274 WARN("Error parsing line '%s'\n", buffer);
275 *p_ret = 0;
276 *p_found = FALSE;
277 return TRUE;
280 *p_ret = (INT)f;
281 return TRUE;
284 /*******************************************************************************
285 * ReadString
287 * Returns FALSE on I/O error or memory allocation failure; sets *p_str to NULL
288 * if line cannot be found or can't be parsed.
291 static BOOL ReadString(FILE *file, CHAR buffer[], INT bufsize, LPCSTR key,
292 LPSTR *p_str)
294 CHAR *cp;
296 if (FindLine(file, buffer, bufsize, key) == FALSE)
297 return FALSE;
299 if (buffer[0] == '\0')
301 *p_str = NULL;
302 return TRUE;
305 cp = buffer + strlen(key); /* first char after key */
306 if (*cp == '\0')
308 *p_str = NULL;
309 return TRUE;
312 while (isspace(*cp)) /* find first non-whitespace char */
313 ++cp;
315 *p_str = HeapAlloc(PSDRV_Heap, 0, strlen(cp) + 1);
316 if (*p_str == NULL)
317 return FALSE;
319 strcpy(*p_str, cp);
320 return TRUE;
323 /*******************************************************************************
324 * ReadBBox
326 * Similar to ReadFloat above.
329 static BOOL ReadBBox(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
330 BOOL *p_found)
332 CHAR *cp, *end_ptr;
333 double d;
335 if (FindLine(file, buffer, bufsize, "FontBBox") == FALSE)
336 return FALSE;
338 if (buffer[0] == '\0')
340 *p_found = FALSE;
341 return TRUE;
344 errno = 0;
346 cp = buffer + sizeof("FontBBox");
347 d = strtod(cp, &end_ptr);
348 if (end_ptr == cp || errno != 0 ||
349 DoubleToFloat(&(afm->FontBBox.llx), d) == FALSE)
350 goto parse_error;
352 cp = end_ptr;
353 d = strtod(cp, &end_ptr);
354 if (end_ptr == cp || errno != 0 ||
355 DoubleToFloat(&(afm->FontBBox.lly), d) == FALSE)
356 goto parse_error;
358 cp = end_ptr;
359 d = strtod(cp, &end_ptr);
360 if (end_ptr == cp || errno != 0
361 || DoubleToFloat(&(afm->FontBBox.urx), d) == FALSE)
362 goto parse_error;
364 cp = end_ptr;
365 d = strtod(cp, &end_ptr);
366 if (end_ptr == cp || errno != 0
367 || DoubleToFloat(&(afm->FontBBox.ury), d) == FALSE)
368 goto parse_error;
370 *p_found = TRUE;
371 return TRUE;
373 parse_error:
374 WARN("Error parsing line '%s'\n", buffer);
375 *p_found = FALSE;
376 return TRUE;
379 /*******************************************************************************
380 * ReadWeight
382 * Finds and parses the 'Weight' line of an AFM file. Only tries to determine
383 * if a font is bold (FW_BOLD) or not (FW_NORMAL) -- ignoring all those cute
384 * little FW_* typedefs in the Win32 doc. AFAICT, this is what the Windows
385 * PostScript driver does.
388 static const struct { LPCSTR keyword; INT weight; } afm_weights[] =
390 { "REGULAR", FW_NORMAL },
391 { "NORMAL", FW_NORMAL },
392 { "ROMAN", FW_NORMAL },
393 { "BOLD", FW_BOLD },
394 { "BOOK", FW_NORMAL },
395 { "MEDIUM", FW_NORMAL },
396 { "LIGHT", FW_NORMAL },
397 { "BLACK", FW_BOLD },
398 { "HEAVY", FW_BOLD },
399 { "DEMI", FW_BOLD },
400 { "ULTRA", FW_BOLD },
401 { "SUPER" , FW_BOLD },
402 { NULL, 0 }
405 static BOOL ReadWeight(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
406 BOOL *p_found)
408 LPSTR sz;
409 CHAR *cp;
410 INT i;
412 if (ReadString(file, buffer, bufsize, "Weight", &sz) == FALSE)
413 return FALSE;
415 if (sz == NULL)
417 *p_found = FALSE;
418 return TRUE;
421 for (cp = sz; *cp != '\0'; ++cp)
422 *cp = toupper(*cp);
424 for (i = 0; afm_weights[i].keyword != NULL; ++i)
426 if (strstr(sz, afm_weights[i].keyword) != NULL)
428 afm->Weight = afm_weights[i].weight;
429 *p_found = TRUE;
430 HeapFree(PSDRV_Heap, 0, sz);
431 return TRUE;
435 WARN("Unknown weight '%s'; treating as Roman\n", sz);
437 afm->Weight = FW_NORMAL;
438 *p_found = TRUE;
439 HeapFree(PSDRV_Heap, 0, sz);
440 return TRUE;
443 /*******************************************************************************
444 * ReadFixedPitch
447 static BOOL ReadFixedPitch(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
448 BOOL *p_found)
450 LPSTR sz;
452 if (ReadString(file, buffer, bufsize, "IsFixedPitch", &sz) == FALSE)
453 return FALSE;
455 if (sz == NULL)
457 *p_found = FALSE;
458 return TRUE;
461 if (strcasecmp(sz, "false") == 0)
463 afm->IsFixedPitch = FALSE;
464 *p_found = TRUE;
465 HeapFree(PSDRV_Heap, 0, sz);
466 return TRUE;
469 if (strcasecmp(sz, "true") == 0)
471 afm->IsFixedPitch = TRUE;
472 *p_found = TRUE;
473 HeapFree(PSDRV_Heap, 0, sz);
474 return TRUE;
477 WARN("Can't parse line '%s'\n", buffer);
479 *p_found = FALSE;
480 HeapFree(PSDRV_Heap, 0, sz);
481 return TRUE;
484 /*******************************************************************************
485 * ReadFontMetrics
487 * Allocates space for the AFM on the driver heap and reads basic font metrics.
488 * Returns FALSE for memory allocation failure; sets *p_afm to NULL if AFM file
489 * is unusable.
492 static BOOL ReadFontMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM **p_afm)
494 AFM *afm;
495 BOOL retval, found;
497 *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
498 if (afm == NULL)
499 return FALSE;
501 retval = ReadWeight(file, buffer, bufsize, afm, &found);
502 if (retval == FALSE || found == FALSE)
503 goto cleanup_afm;
505 retval = ReadFloat(file, buffer, bufsize, "ItalicAngle",
506 &(afm->ItalicAngle), &found);
507 if (retval == FALSE || found == FALSE)
508 goto cleanup_afm;
510 retval = ReadFixedPitch(file, buffer, bufsize, afm, &found);
511 if (retval == FALSE || found == FALSE)
512 goto cleanup_afm;
514 retval = ReadBBox(file, buffer, bufsize, afm, &found);
515 if (retval == FALSE || found == FALSE)
516 goto cleanup_afm;
518 retval = ReadFloat(file, buffer, bufsize, "UnderlinePosition",
519 &(afm->UnderlinePosition), &found);
520 if (retval == FALSE || found == FALSE)
521 goto cleanup_afm;
523 retval = ReadFloat(file, buffer, bufsize, "UnderlineThickness",
524 &(afm->UnderlineThickness), &found);
525 if (retval == FALSE || found == FALSE)
526 goto cleanup_afm;
528 retval = ReadFloat(file, buffer, bufsize, "Ascender", /* optional */
529 &(afm->Ascender), &found);
530 if (retval == FALSE)
531 goto cleanup_afm;
533 retval = ReadFloat(file, buffer, bufsize, "Descender", /* optional */
534 &(afm->Descender), &found);
535 if (retval == FALSE)
536 goto cleanup_afm;
538 afm->WinMetrics.usUnitsPerEm = 1000;
539 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->Ascender);
540 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->Descender);
542 if (afm->WinMetrics.sTypoAscender == 0)
543 afm->WinMetrics.sTypoAscender = (SHORT)Round(afm->FontBBox.ury);
545 if (afm->WinMetrics.sTypoDescender == 0)
546 afm->WinMetrics.sTypoDescender = (SHORT)Round(afm->FontBBox.lly);
548 afm->WinMetrics.sTypoLineGap = 1200 -
549 (afm->WinMetrics.sTypoAscender - afm->WinMetrics.sTypoDescender);
550 if (afm->WinMetrics.sTypoLineGap < 0)
551 afm->WinMetrics.sTypoLineGap = 0;
553 return TRUE;
555 cleanup_afm: /* handle fatal or non-fatal errors */
556 HeapFree(PSDRV_Heap, 0, afm);
557 *p_afm = NULL;
558 return retval;
561 /*******************************************************************************
562 * ParseC
564 * Fatal error: return FALSE (none defined)
566 * Non-fatal error: leave metrics->C set to INT_MAX
569 static BOOL ParseC(LPSTR sz, OLD_AFMMETRICS *metrics)
571 int base = 10;
572 long l;
573 CHAR *cp, *end_ptr;
575 cp = sz + 1;
577 if (*cp == 'H')
579 base = 16;
580 ++cp;
583 errno = 0;
584 l = strtol(cp, &end_ptr, base);
585 if (end_ptr == cp || errno != 0 || l > INT_MAX || l < INT_MIN)
587 WARN("Error parsing character code '%s'\n", sz);
588 return TRUE;
591 metrics->C = (INT)l;
592 return TRUE;
595 /*******************************************************************************
596 * ParseW
598 * Fatal error: return FALSE (none defined)
600 * Non-fatal error: leave metrics->WX set to FLT_MAX
603 static BOOL ParseW(LPSTR sz, OLD_AFMMETRICS *metrics)
605 CHAR *cp, *end_ptr;
606 BOOL vector = TRUE;
607 double d;
609 cp = sz + 1;
611 if (*cp == '0')
612 ++cp;
614 if (*cp == 'X')
616 vector = FALSE;
617 ++cp;
620 if (!isspace(*cp))
621 goto parse_error;
623 errno = 0;
624 d = strtod(cp, &end_ptr);
625 if (end_ptr == cp || errno != 0 ||
626 DoubleToFloat(&(metrics->WX), d) == FALSE)
627 goto parse_error;
629 if (vector == FALSE)
630 return TRUE;
632 /* Make sure that Y component of vector is zero */
634 d = strtod(cp, &end_ptr); /* errno == 0 */
635 if (end_ptr == cp || errno != 0 || d != 0.0)
637 metrics->WX = FLT_MAX;
638 goto parse_error;
641 return TRUE;
643 parse_error:
644 WARN("Error parsing character width '%s'\n", sz);
645 return TRUE;
648 /*******************************************************************************
650 * ParseB
652 * Fatal error: return FALSE (none defined)
654 * Non-fatal error: leave metrics->B.ury set to FLT_MAX
657 static BOOL ParseB(LPSTR sz, OLD_AFMMETRICS *metrics)
659 CHAR *cp, *end_ptr;
660 double d;
662 errno = 0;
664 cp = sz + 1;
665 d = strtod(cp, &end_ptr);
666 if (end_ptr == cp || errno != 0 ||
667 DoubleToFloat(&(metrics->B.llx), d) == FALSE)
668 goto parse_error;
670 cp = end_ptr;
671 d = strtod(cp, &end_ptr);
672 if (end_ptr == cp || errno != 0 ||
673 DoubleToFloat(&(metrics->B.lly), d) == FALSE)
674 goto parse_error;
676 cp = end_ptr;
677 d = strtod(cp, &end_ptr);
678 if (end_ptr == cp || errno != 0 ||
679 DoubleToFloat(&(metrics->B.urx), d) == FALSE)
680 goto parse_error;
682 cp = end_ptr;
683 d = strtod(cp, &end_ptr);
684 if (end_ptr == cp || errno != 0 ||
685 DoubleToFloat(&(metrics->B.ury), d) == FALSE)
686 goto parse_error;
688 return TRUE;
690 parse_error:
691 WARN("Error parsing glyph bounding box '%s'\n", sz);
692 return TRUE;
695 /*******************************************************************************
696 * ParseN
698 * Fatal error: return FALSE (PSDRV_GlyphName failure)
700 * Non-fatal error: leave metrics-> set to NULL
703 static BOOL ParseN(LPSTR sz, OLD_AFMMETRICS *metrics)
705 CHAR save, *cp, *end_ptr;
707 cp = sz + 1;
709 while (isspace(*cp))
710 ++cp;
712 end_ptr = cp;
714 while (*end_ptr != '\0' && !isspace(*end_ptr))
715 ++end_ptr;
717 if (end_ptr == cp)
719 WARN("Error parsing glyph name '%s'\n", sz);
720 return TRUE;
723 save = *end_ptr;
724 *end_ptr = '\0';
726 metrics->N = PSDRV_GlyphName(cp);
727 if (metrics->N == NULL)
728 return FALSE;
730 *end_ptr = save;
731 return TRUE;
734 /*******************************************************************************
735 * ParseCharMetrics
737 * Parses the metrics line for a single glyph in an AFM file. Returns FALSE on
738 * fatal error; sets *metrics to 'badmetrics' on non-fatal error.
741 static const OLD_AFMMETRICS badmetrics =
743 INT_MAX, /* C */
744 LONG_MAX, /* UV */
745 FLT_MAX, /* WX */
746 NULL, /* N */
747 { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }, /* B */
748 NULL /* L */
751 static BOOL ParseCharMetrics(LPSTR buffer, INT len, OLD_AFMMETRICS *metrics)
753 CHAR *cp = buffer;
755 *metrics = badmetrics;
757 while (*cp != '\0')
759 while (isspace(*cp))
760 ++cp;
762 switch(*cp)
764 case 'C': if (ParseC(cp, metrics) == FALSE)
765 return FALSE;
766 break;
768 case 'W': if (ParseW(cp, metrics) == FALSE)
769 return FALSE;
770 break;
772 case 'N': if (ParseN(cp, metrics) == FALSE)
773 return FALSE;
774 break;
776 case 'B': if (ParseB(cp, metrics) == FALSE)
777 return FALSE;
778 break;
781 cp = strchr(cp, ';');
782 if (cp == NULL)
784 WARN("No terminating semicolon\n");
785 break;
788 ++cp;
791 if (metrics->C == INT_MAX || metrics->WX == FLT_MAX || metrics->N == NULL ||
792 metrics->B.ury == FLT_MAX)
794 *metrics = badmetrics;
795 return TRUE;
798 return TRUE;
801 /*******************************************************************************
802 * IsWinANSI
804 * Checks whether Unicode value is part of Microsoft code page 1252
807 static const LONG ansiChars[21] =
809 0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017d, 0x017e, 0x0192, 0x02c6,
810 0x02c9, 0x02dc, 0x03bc, 0x2013, 0x2014, 0x2026, 0x2030, 0x2039, 0x203a,
811 0x20ac, 0x2122, 0x2219
814 static int cmpUV(const void *a, const void *b)
816 return (int)(*((const LONG *)a) - *((const LONG *)b));
819 inline static BOOL IsWinANSI(LONG uv)
821 if ((0x0020 <= uv && uv <= 0x007e) || (0x00a0 <= uv && uv <= 0x00ff) ||
822 (0x2018 <= uv && uv <= 0x201a) || (0x201c <= uv && uv <= 0x201e) ||
823 (0x2020 <= uv && uv <= 0x2022))
824 return TRUE;
826 if (bsearch(&uv, ansiChars, 21, sizeof(INT), cmpUV) != NULL)
827 return TRUE;
829 return FALSE;
832 /*******************************************************************************
833 * Unicodify
835 * Determines Unicode value (UV) for each glyph, based on font encoding.
837 * FontSpecific: Usable encodings (0x20 - 0xff) are mapped into the
838 * Unicode private use range U+F020 - U+F0FF.
840 * other: UV determined by glyph name, based on Adobe Glyph List.
842 * Also does some font metric calculations that require UVs to be known.
845 static int UnicodeGlyphByNameIndex(const void *a, const void *b)
847 return ((const UNICODEGLYPH *)a)->name->index -
848 ((const UNICODEGLYPH *)b)->name->index;
851 static VOID Unicodify(AFM *afm, OLD_AFMMETRICS *metrics)
853 INT i;
855 if (strcmp(afm->EncodingScheme, "FontSpecific") == 0)
857 for (i = 0; i < afm->NumofMetrics; ++i)
859 if (metrics[i].C >= 0x20 && metrics[i].C <= 0xff)
861 metrics[i].UV = ((LONG)(metrics[i].C)) | 0xf000L;
863 else
865 TRACE("Unencoded glyph '%s'\n", metrics[i].N->sz);
866 metrics[i].UV = -1L;
870 afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
871 afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
873 else /* non-FontSpecific encoding */
875 UNICODEGLYPH ug, *p_ug;
877 PSDRV_IndexGlyphList(); /* for fast searching of glyph names */
879 afm->WinMetrics.sAscender = afm->WinMetrics.sDescender = 0;
881 for (i = 0; i < afm->NumofMetrics; ++i)
883 ug.name = metrics[i].N;
884 p_ug = bsearch(&ug, PSDRV_AGLbyName, PSDRV_AGLbyNameSize,
885 sizeof(ug), UnicodeGlyphByNameIndex);
886 if (p_ug == NULL)
888 TRACE("Glyph '%s' not in Adobe Glyph List\n", ug.name->sz);
889 metrics[i].UV = -1L;
891 else
893 metrics[i].UV = p_ug->UV;
895 if (IsWinANSI(p_ug->UV))
897 SHORT ury = (SHORT)Round(metrics[i].B.ury);
898 SHORT lly = (SHORT)Round(metrics[i].B.lly);
900 if (ury > afm->WinMetrics.sAscender)
901 afm->WinMetrics.sAscender = ury;
902 if (lly < afm->WinMetrics.sDescender)
903 afm->WinMetrics.sDescender = lly;
908 if (afm->WinMetrics.sAscender == 0)
909 afm->WinMetrics.sAscender = (SHORT)Round(afm->FontBBox.ury);
910 if (afm->WinMetrics.sDescender == 0)
911 afm->WinMetrics.sDescender = (SHORT)Round(afm->FontBBox.lly);
914 afm->WinMetrics.sLineGap =
915 1150 - (afm->WinMetrics.sAscender - afm->WinMetrics.sDescender);
916 if (afm->WinMetrics.sLineGap < 0)
917 afm->WinMetrics.sLineGap = 0;
919 afm->WinMetrics.usWinAscent = (afm->WinMetrics.sAscender > 0) ?
920 afm->WinMetrics.sAscender : 0;
921 afm->WinMetrics.usWinDescent = (afm->WinMetrics.sDescender < 0) ?
922 -(afm->WinMetrics.sDescender) : 0;
925 /*******************************************************************************
926 * ReadCharMetrics
928 * Reads metrics for all glyphs. *p_metrics will be NULL on non-fatal error.
931 static int OldAFMMetricsByUV(const void *a, const void *b)
933 return ((const OLD_AFMMETRICS *)a)->UV - ((const OLD_AFMMETRICS *)b)->UV;
936 static BOOL ReadCharMetrics(FILE *file, CHAR buffer[], INT bufsize, AFM *afm,
937 AFMMETRICS **p_metrics)
939 BOOL retval, found;
940 OLD_AFMMETRICS *old_metrics, *encoded_metrics;
941 AFMMETRICS *metrics;
942 INT i, len;
944 retval = ReadInt(file, buffer, bufsize, "StartCharMetrics",
945 &(afm->NumofMetrics), &found);
946 if (retval == FALSE || found == FALSE)
948 *p_metrics = NULL;
949 return retval;
952 old_metrics = HeapAlloc(PSDRV_Heap, 0,
953 afm->NumofMetrics * sizeof(*old_metrics));
954 if (old_metrics == NULL)
955 return FALSE;
957 for (i = 0; i < afm->NumofMetrics; ++i)
959 retval = ReadLine(file, buffer, bufsize, &len);
960 if (retval == FALSE)
961 goto cleanup_old_metrics;
963 if(len > 0)
965 retval = ParseCharMetrics(buffer, len, old_metrics + i);
966 if (retval == FALSE || old_metrics[i].C == INT_MAX)
967 goto cleanup_old_metrics;
969 continue;
972 switch (len)
974 case 0: --i;
975 continue;
977 case INT_MIN: WARN("Ignoring long line '%32s...'\n", buffer);
978 goto cleanup_old_metrics; /* retval == TRUE */
980 case EOF: WARN("Unexpected EOF\n");
981 goto cleanup_old_metrics; /* retval == TRUE */
985 Unicodify(afm, old_metrics); /* wait until glyph names have been read */
987 qsort(old_metrics, afm->NumofMetrics, sizeof(*old_metrics),
988 OldAFMMetricsByUV);
990 for (i = 0; old_metrics[i].UV == -1; ++i); /* count unencoded glyphs */
992 afm->NumofMetrics -= i;
993 encoded_metrics = old_metrics + i;
995 afm->Metrics = *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0,
996 afm->NumofMetrics * sizeof(*metrics));
997 if (afm->Metrics == NULL)
998 goto cleanup_old_metrics; /* retval == TRUE */
1000 for (i = 0; i < afm->NumofMetrics; ++i, ++metrics, ++encoded_metrics)
1002 metrics->C = encoded_metrics->C;
1003 metrics->UV = encoded_metrics->UV;
1004 metrics->WX = encoded_metrics->WX;
1005 metrics->N = encoded_metrics->N;
1008 HeapFree(PSDRV_Heap, 0, old_metrics);
1010 afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
1012 return TRUE;
1014 cleanup_old_metrics: /* handle fatal or non-fatal errors */
1015 HeapFree(PSDRV_Heap, 0, old_metrics);
1016 *p_metrics = NULL;
1017 return retval;
1020 /*******************************************************************************
1021 * BuildAFM
1023 * Builds the AFM for a PostScript font and adds it to the driver font list.
1024 * Returns FALSE only on an unexpected error (memory allocation or I/O error).
1027 static BOOL BuildAFM(FILE *file)
1029 CHAR buffer[258]; /* allow for <cr>, <lf>, and <nul> */
1030 AFM *afm;
1031 AFMMETRICS *metrics;
1032 LPSTR font_name, full_name, family_name, encoding_scheme;
1033 BOOL retval, added;
1035 retval = ReadFontMetrics(file, buffer, sizeof(buffer), &afm);
1036 if (retval == FALSE || afm == NULL)
1037 return retval;
1039 retval = ReadString(file, buffer, sizeof(buffer), "FontName", &font_name);
1040 if (retval == FALSE || font_name == NULL)
1041 goto cleanup_afm;
1043 retval = ReadString(file, buffer, sizeof(buffer), "FullName", &full_name);
1044 if (retval == FALSE || full_name == NULL)
1045 goto cleanup_font_name;
1047 retval = ReadString(file, buffer, sizeof(buffer), "FamilyName",
1048 &family_name);
1049 if (retval == FALSE || family_name == NULL)
1050 goto cleanup_full_name;
1052 retval = ReadString(file, buffer, sizeof(buffer), "EncodingScheme",
1053 &encoding_scheme);
1054 if (retval == FALSE || encoding_scheme == NULL)
1055 goto cleanup_family_name;
1057 afm->FontName = font_name;
1058 afm->FullName = full_name;
1059 afm->FamilyName = family_name;
1060 afm->EncodingScheme = encoding_scheme;
1062 retval = ReadCharMetrics(file, buffer, sizeof(buffer), afm, &metrics);
1063 if (retval == FALSE || metrics == FALSE)
1064 goto cleanup_encoding_scheme;
1066 retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
1067 if (retval == FALSE || added == FALSE)
1068 goto cleanup_encoding_scheme;
1070 return TRUE;
1072 /* clean up after fatal or non-fatal errors */
1074 cleanup_encoding_scheme:
1075 HeapFree(PSDRV_Heap, 0, encoding_scheme);
1076 cleanup_family_name:
1077 HeapFree(PSDRV_Heap, 0, family_name);
1078 cleanup_full_name:
1079 HeapFree(PSDRV_Heap, 0, full_name);
1080 cleanup_font_name:
1081 HeapFree(PSDRV_Heap, 0, font_name);
1082 cleanup_afm:
1083 HeapFree(PSDRV_Heap, 0, afm);
1085 return retval;
1088 /*******************************************************************************
1089 * ReadAFMFile
1091 * Reads font metrics from Type 1 AFM file. Only returns FALSE for
1092 * unexpected errors (memory allocation or I/O).
1095 static BOOL ReadAFMFile(LPCSTR filename)
1097 FILE *f;
1098 BOOL retval;
1100 TRACE("%s\n", filename);
1102 f = fopen(filename, "r");
1103 if (f == NULL)
1105 WARN("%s: %s\n", filename, strerror(errno));
1106 return TRUE;
1109 retval = BuildAFM(f);
1111 fclose(f);
1112 return retval;
1115 /*******************************************************************************
1116 * ReadAFMDir
1118 * Reads all Type 1 AFM files in a directory.
1121 static BOOL ReadAFMDir(LPCSTR dirname)
1123 struct dirent *dent;
1124 DIR *dir;
1125 CHAR filename[256];
1127 dir = opendir(dirname);
1128 if (dir == NULL)
1130 WARN("%s: %s\n", dirname, strerror(errno));
1131 return TRUE;
1134 while ((dent = readdir(dir)) != NULL)
1136 CHAR *file_extension = strchr(dent->d_name, '.');
1137 int fn_len;
1139 if (file_extension == NULL || strcasecmp(file_extension, ".afm") != 0)
1140 continue;
1142 fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
1143 if (fn_len < 0 || fn_len > sizeof(filename) - 1)
1145 WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
1146 continue;
1149 if (ReadAFMFile(filename) == FALSE)
1151 closedir(dir);
1152 return FALSE;
1156 closedir(dir);
1157 return TRUE;
1160 /*******************************************************************************
1161 * PSDRV_GetType1Metrics
1163 * Reads font metrics from Type 1 AFM font files in directories listed in the
1164 * [afmdirs] section of the Wine configuration file.
1166 * If this function fails (returns FALSE), the dirver will fail to initialize
1167 * and the driver heap will be destroyed, so it's not necessary to HeapFree
1168 * everything in that event.
1171 BOOL PSDRV_GetType1Metrics(void)
1173 CHAR name_buf[256], value_buf[256];
1174 INT i = 0;
1175 HKEY hkey;
1176 DWORD type, name_len, value_len;
1178 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1179 "Software\\Wine\\Wine\\Config\\afmdirs",
1180 0, KEY_READ, &hkey) != ERROR_SUCCESS)
1181 return TRUE;
1183 name_len = sizeof(name_buf);
1184 value_len = sizeof(value_buf);
1186 while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf,
1187 &value_len) == ERROR_SUCCESS)
1189 value_buf[sizeof(value_buf) - 1] = '\0';
1191 if (ReadAFMDir(value_buf) == FALSE)
1193 RegCloseKey(hkey);
1194 return FALSE;
1197 /* initialize lengths for new iteration */
1199 name_len = sizeof(name_buf);
1200 value_len = sizeof(value_buf);
1203 RegCloseKey(hkey);
1204 return TRUE;