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.
35 #include <limits.h> /* INT_MIN */
36 #include <float.h> /* FLT_MAX */
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(psdrv
);
49 /*******************************************************************************
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
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
)
74 if (fgets(buffer
, bufsize
, file
) == NULL
)
76 if (feof(file
) == 0) /* EOF or error? */
78 ERR("%s\n", strerror(errno
));
86 cp
= strchr(buffer
, '\n');
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
);
99 if (feof(file
) == 0) /* EOF or error? */
101 ERR("%s\n", strerror(errno
));
105 WARN("No newline at EOF\n");
111 else /* no newline and not truncated */
113 if (strcmp(buffer
, "\x1a") == 0) /* test for DOS EOF */
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 */
128 break; /* don't underflow buffer */
131 while (isspace(*cp
));
133 *p_result
= strlen(buffer
);
137 /*******************************************************************************
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
);
158 ok
= ReadLine(file
, buffer
, bufsize
, &result
);
162 if (result
> 0 && strncmp(buffer
, key
, len
) == 0)
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
);
181 /*******************************************************************************
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
189 static inline BOOL
DoubleToFloat(float *p_f
, double d
)
191 if (d
> (double)FLT_MAX
|| d
< -(double)FLT_MAX
)
198 /*******************************************************************************
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 /*******************************************************************************
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
)
224 if (FindLine(file
, buffer
, bufsize
, key
) == FALSE
)
227 if (buffer
[0] == '\0') /* line not found */
234 cp
= buffer
+ strlen(key
); /* first char after key */
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
);
250 /*******************************************************************************
253 * See description of ReadFloat.
256 static BOOL
ReadInt(FILE *file
, CHAR buffer
[], INT bufsize
, LPCSTR key
,
257 INT
*p_ret
, BOOL
*p_found
)
262 retval
= ReadFloat(file
, buffer
, bufsize
, key
, &f
, p_found
);
263 if (retval
== FALSE
|| *p_found
== FALSE
)
271 if (f
> (FLOAT
)INT_MAX
|| f
< (FLOAT
)INT_MIN
)
273 WARN("Error parsing line '%s'\n", buffer
);
283 /*******************************************************************************
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
,
295 if (FindLine(file
, buffer
, bufsize
, key
) == FALSE
)
298 if (buffer
[0] == '\0')
304 cp
= buffer
+ strlen(key
); /* first char after key */
311 while (isspace(*cp
)) /* find first non-whitespace char */
314 *p_str
= HeapAlloc(PSDRV_Heap
, 0, strlen(cp
) + 1);
322 static BOOL
ReadStringW(FILE *file
, CHAR
*buffer
, int size
, const char *key
, WCHAR
**out
)
327 if (!ReadString(file
, buffer
, size
, key
, &outA
))
335 len
= MultiByteToWideChar(CP_ACP
, 0, outA
, -1, NULL
, 0);
337 *out
= HeapAlloc(PSDRV_Heap
, 0, len
* sizeof(WCHAR
));
340 HeapFree(PSDRV_Heap
, 0, outA
);
343 MultiByteToWideChar(CP_ACP
, 0, outA
, -1, *out
, len
);
344 HeapFree(PSDRV_Heap
, 0, outA
);
348 /*******************************************************************************
351 * Similar to ReadFloat above.
354 static BOOL
ReadBBox(FILE *file
, CHAR buffer
[], INT bufsize
, AFM
*afm
,
360 if (FindLine(file
, buffer
, bufsize
, "FontBBox") == FALSE
)
363 if (buffer
[0] == '\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
)
378 d
= strtod(cp
, &end_ptr
);
379 if (end_ptr
== cp
|| errno
!= 0 ||
380 DoubleToFloat(&(afm
->FontBBox
.lly
), d
) == FALSE
)
384 d
= strtod(cp
, &end_ptr
);
385 if (end_ptr
== cp
|| errno
!= 0
386 || DoubleToFloat(&(afm
->FontBBox
.urx
), d
) == FALSE
)
390 d
= strtod(cp
, &end_ptr
);
391 if (end_ptr
== cp
|| errno
!= 0
392 || DoubleToFloat(&(afm
->FontBBox
.ury
), d
) == FALSE
)
399 WARN("Error parsing line '%s'\n", buffer
);
404 /*******************************************************************************
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
},
419 { "BOOK", FW_NORMAL
},
420 { "MEDIUM", FW_NORMAL
},
421 { "LIGHT", FW_NORMAL
},
422 { "BLACK", FW_BOLD
},
423 { "HEAVY", FW_BOLD
},
425 { "ULTRA", FW_BOLD
},
426 { "SUPER" , FW_BOLD
},
430 static BOOL
ReadWeight(FILE *file
, CHAR buffer
[], INT bufsize
, AFM
*afm
,
437 if (ReadString(file
, buffer
, bufsize
, "Weight", &sz
) == FALSE
)
446 for (cp
= sz
; *cp
!= '\0'; ++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
;
455 HeapFree(PSDRV_Heap
, 0, sz
);
460 WARN("Unknown weight '%s'; treating as Roman\n", sz
);
462 afm
->Weight
= FW_NORMAL
;
464 HeapFree(PSDRV_Heap
, 0, sz
);
468 /*******************************************************************************
472 static BOOL
ReadFixedPitch(FILE *file
, CHAR buffer
[], INT bufsize
, AFM
*afm
,
477 if (ReadString(file
, buffer
, bufsize
, "IsFixedPitch", &sz
) == FALSE
)
486 if (stricmp(sz
, "false") == 0)
488 afm
->IsFixedPitch
= FALSE
;
490 HeapFree(PSDRV_Heap
, 0, sz
);
494 if (stricmp(sz
, "true") == 0)
496 afm
->IsFixedPitch
= TRUE
;
498 HeapFree(PSDRV_Heap
, 0, sz
);
502 WARN("Can't parse line '%s'\n", buffer
);
505 HeapFree(PSDRV_Heap
, 0, sz
);
509 /*******************************************************************************
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
517 static BOOL
ReadFontMetrics(FILE *file
, CHAR buffer
[], INT bufsize
, AFM
**p_afm
)
522 *p_afm
= afm
= HeapAlloc(PSDRV_Heap
, 0, sizeof(*afm
));
526 retval
= ReadWeight(file
, buffer
, bufsize
, afm
, &found
);
527 if (retval
== FALSE
|| found
== FALSE
)
530 retval
= ReadFloat(file
, buffer
, bufsize
, "ItalicAngle",
531 &(afm
->ItalicAngle
), &found
);
532 if (retval
== FALSE
|| found
== FALSE
)
535 retval
= ReadFixedPitch(file
, buffer
, bufsize
, afm
, &found
);
536 if (retval
== FALSE
|| found
== FALSE
)
539 retval
= ReadBBox(file
, buffer
, bufsize
, afm
, &found
);
540 if (retval
== FALSE
|| found
== FALSE
)
543 retval
= ReadFloat(file
, buffer
, bufsize
, "UnderlinePosition",
544 &(afm
->UnderlinePosition
), &found
);
545 if (retval
== FALSE
|| found
== FALSE
)
548 retval
= ReadFloat(file
, buffer
, bufsize
, "UnderlineThickness",
549 &(afm
->UnderlineThickness
), &found
);
550 if (retval
== FALSE
|| found
== FALSE
)
553 retval
= ReadFloat(file
, buffer
, bufsize
, "Ascender", /* optional */
554 &(afm
->Ascender
), &found
);
558 retval
= ReadFloat(file
, buffer
, bufsize
, "Descender", /* optional */
559 &(afm
->Descender
), &found
);
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;
580 cleanup_afm
: /* handle fatal or non-fatal errors */
581 HeapFree(PSDRV_Heap
, 0, afm
);
586 /*******************************************************************************
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
)
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
);
620 /*******************************************************************************
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
)
649 d
= strtod(cp
, &end_ptr
);
650 if (end_ptr
== cp
|| errno
!= 0 ||
651 DoubleToFloat(&(metrics
->WX
), d
) == FALSE
)
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
;
669 WARN("Error parsing character width '%s'\n", sz
);
673 /*******************************************************************************
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
)
690 d
= strtod(cp
, &end_ptr
);
691 if (end_ptr
== cp
|| errno
!= 0 ||
692 DoubleToFloat(&(metrics
->B
.llx
), d
) == FALSE
)
696 d
= strtod(cp
, &end_ptr
);
697 if (end_ptr
== cp
|| errno
!= 0 ||
698 DoubleToFloat(&(metrics
->B
.lly
), d
) == FALSE
)
702 d
= strtod(cp
, &end_ptr
);
703 if (end_ptr
== cp
|| errno
!= 0 ||
704 DoubleToFloat(&(metrics
->B
.urx
), d
) == FALSE
)
708 d
= strtod(cp
, &end_ptr
);
709 if (end_ptr
== cp
|| errno
!= 0 ||
710 DoubleToFloat(&(metrics
->B
.ury
), d
) == FALSE
)
716 WARN("Error parsing glyph bounding box '%s'\n", sz
);
720 /*******************************************************************************
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
;
739 while (*end_ptr
!= '\0' && !isspace(*end_ptr
))
744 WARN("Error parsing glyph name '%s'\n", sz
);
751 metrics
->N
= PSDRV_GlyphName(cp
);
752 if (metrics
->N
== NULL
)
759 /*******************************************************************************
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
=
772 { FLT_MAX
, FLT_MAX
, FLT_MAX
, FLT_MAX
}, /* B */
776 static BOOL
ParseCharMetrics(LPSTR buffer
, INT len
, OLD_AFMMETRICS
*metrics
)
780 *metrics
= badmetrics
;
789 case 'C': if (ParseC(cp
, metrics
) == FALSE
)
793 case 'W': if (ParseW(cp
, metrics
) == FALSE
)
797 case 'N': if (ParseN(cp
, metrics
) == FALSE
)
801 case 'B': if (ParseB(cp
, metrics
) == FALSE
)
806 cp
= strchr(cp
, ';');
809 WARN("No terminating semicolon\n");
816 if (metrics
->C
== INT_MAX
|| metrics
->WX
== FLT_MAX
|| metrics
->N
== NULL
||
817 metrics
->B
.ury
== FLT_MAX
)
819 *metrics
= badmetrics
;
826 /*******************************************************************************
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))
851 if (bsearch(&uv
, ansiChars
, 21, sizeof(INT
), cmpUV
) != NULL
)
857 /*******************************************************************************
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
)
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
;
890 TRACE("Unencoded glyph '%s'\n", metrics
[i
].N
->sz
);
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
);
913 TRACE("Glyph '%s' not in Adobe Glyph List\n", ug
.name
->sz
);
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 /*******************************************************************************
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
)
965 OLD_AFMMETRICS
*old_metrics
, *encoded_metrics
;
969 retval
= ReadInt(file
, buffer
, bufsize
, "StartCharMetrics",
970 &(afm
->NumofMetrics
), &found
);
971 if (retval
== FALSE
|| found
== FALSE
)
977 old_metrics
= HeapAlloc(PSDRV_Heap
, 0,
978 afm
->NumofMetrics
* sizeof(*old_metrics
));
979 if (old_metrics
== NULL
)
982 for (i
= 0; i
< afm
->NumofMetrics
; ++i
)
984 retval
= ReadLine(file
, buffer
, bufsize
, &len
);
986 goto cleanup_old_metrics
;
990 retval
= ParseCharMetrics(buffer
, len
, old_metrics
+ i
);
991 if (retval
== FALSE
|| old_metrics
[i
].C
== INT_MAX
)
992 goto cleanup_old_metrics
;
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
),
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
);
1039 cleanup_old_metrics
: /* handle fatal or non-fatal errors */
1040 HeapFree(PSDRV_Heap
, 0, old_metrics
);
1045 /*******************************************************************************
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> */
1056 AFMMETRICS
*metrics
;
1057 WCHAR
*full_name
, *family_name
, *encoding_scheme
;
1061 retval
= ReadFontMetrics(file
, buffer
, sizeof(buffer
), &afm
);
1062 if (retval
== FALSE
|| afm
== NULL
)
1065 retval
= ReadString(file
, buffer
, sizeof(buffer
), "FontName", &font_name
);
1066 if (retval
== FALSE
|| font_name
== NULL
)
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",
1075 if (retval
== FALSE
|| family_name
== NULL
)
1076 goto cleanup_full_name
;
1078 retval
= ReadStringW(file
, buffer
, sizeof(buffer
), "EncodingScheme",
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
;
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
);
1105 HeapFree(PSDRV_Heap
, 0, full_name
);
1107 HeapFree(PSDRV_Heap
, 0, font_name
);
1109 HeapFree(PSDRV_Heap
, 0, afm
);
1114 /*******************************************************************************
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
)
1126 TRACE("%s\n", debugstr_w(filename
));
1128 f
= _wfopen(filename
, L
"r");
1131 WARN("%s: %s\n", debugstr_w(filename
), strerror(errno
));
1135 retval
= BuildAFM(f
);
1141 /*******************************************************************************
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
;
1152 WCHAR
*p
, *filename
;
1155 if (!path
) return TRUE
;
1156 if (!(filename
= HeapAlloc( GetProcessHeap(), 0, lstrlenW(path
) + 256 )))
1158 HeapFree( GetProcessHeap(), 0, path
);
1161 lstrcpyW( filename
, path
);
1162 HeapFree( GetProcessHeap(), 0, path
);
1163 p
= filename
+ lstrlenW(filename
);
1165 lstrcpyW( p
, L
"*" );
1167 handle
= _wfindfirst( filename
, &data
);
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
);
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)
1202 /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
1203 if (RegOpenKeyA(HKEY_CURRENT_USER
, "Software\\Wine\\Fonts", &hkey
) != ERROR_SUCCESS
)
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
) );
1219 LPSTR next
= strchr( ptr
, ':' );
1220 if (next
) *next
++ = 0;
1221 if (!ReadAFMDir( ptr
))
1228 HeapFree( PSDRV_Heap
, 0, valueA
);
1230 HeapFree( PSDRV_Heap
, 0, valueW
);