2 * Adobe Font Metric (AFM) file parsing
3 * See http://www.adobe.com/supportservice/devrelations/PDFS/TN/5004.AFM_Spec.pdf
5 * Copyright 1998 Huw D M Davies
11 #include "winnt.h" /* HEAP_ZERO_MEMORY */
14 #include "debugtools.h"
17 DEFAULT_DEBUG_CHANNEL(psdrv
);
20 /* ptr to fonts for which we have afm files */
21 FONTFAMILY
*PSDRV_AFMFontList
= NULL
;
24 /***********************************************************
26 * PSDRV_AFMGetCharMetrics
28 * Parses CharMetric section of AFM file.
30 * Actually only collects the widths of numbered chars and puts then in
33 static void PSDRV_AFMGetCharMetrics(AFM
*afm
, FILE *fp
)
35 char line
[256], valbuf
[256];
36 char *cp
, *item
, *value
, *curpos
, *endpos
;
38 AFMMETRICS
**insert
= &afm
->Metrics
, *metric
;
40 for(i
= 0; i
< afm
->NumofMetrics
; i
++) {
42 if(!fgets(line
, sizeof(line
), fp
)) {
43 ERR("Unexpected EOF\n");
47 metric
= *insert
= HeapAlloc( PSDRV_Heap
, HEAP_ZERO_MEMORY
,
49 insert
= &metric
->next
;
51 cp
= line
+ strlen(line
);
55 } while(cp
> line
&& isspace(*cp
));
62 value
= strpbrk(item
, " \t");
63 while(isspace(*value
))
65 cp
= endpos
= strchr(value
, ';');
68 memcpy(valbuf
, value
, cp
- value
+ 1);
69 valbuf
[cp
- value
+ 1] = '\0';
72 if(!strncmp(item
, "C ", 2)) {
73 value
= strchr(item
, ' ');
74 sscanf(value
, " %d", &metric
->C
);
76 } else if(!strncmp(item
, "CH ", 3)) {
77 value
= strrchr(item
, ' ');
78 sscanf(value
, " %x", &metric
->C
);
81 else if(!strncmp("WX ", item
, 3) || !strncmp("W0X ", item
, 4)) {
82 sscanf(value
, "%f", &metric
->WX
);
83 if(metric
->C
>= 0 && metric
->C
<= 0xff)
84 afm
->CharWidths
[metric
->C
] = metric
->WX
;
87 else if(!strncmp("N ", item
, 2)) {
88 metric
->N
= HEAP_strdupA( PSDRV_Heap
, 0, value
);
91 else if(!strncmp("B ", item
, 2)) {
92 sscanf(value
, "%f%f%f%f", &metric
->B
.llx
, &metric
->B
.lly
,
93 &metric
->B
.urx
, &metric
->B
.ury
);
95 /* Store height of Aring to use as lfHeight */
96 if(metric
->N
&& !strncmp(metric
->N
, "Aring", 5))
97 afm
->FullAscender
= metric
->B
.ury
;
100 /* Ligatures go here... */
106 TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n",
107 metric
->N
, metric
->WX
, metric
->B
.llx
, metric
->B
.lly
,
108 metric
->B
.urx
, metric
->B
.ury
);
115 /***********************************************************
119 * Fills out an AFM structure and associated substructures (see psdrv.h)
120 * for a given AFM file. All memory is allocated from the process heap.
121 * Returns a ptr to the AFM structure or NULL on error.
123 * This is not complete (we don't handle kerning yet) and not efficient
125 static AFM
*PSDRV_AFMParse(char const *file
)
133 TRACE("parsing '%s'\n", file
);
135 if((fp
= fopen(file
, "r")) == NULL
) {
136 MESSAGE("Can't open AFM file '%s'. Please check wine.conf .\n", file
);
140 afm
= HeapAlloc(PSDRV_Heap
, HEAP_ZERO_MEMORY
, sizeof(AFM
));
146 while(fgets(buf
, sizeof(buf
), fp
)) {
147 cp
= buf
+ strlen(buf
);
151 } while(cp
> buf
&& isspace(*cp
));
153 value
= strchr(buf
, ' ');
155 while(isspace(*value
))
158 if(!strncmp("FontName", buf
, 8)) {
159 afm
->FontName
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
163 if(!strncmp("FullName", buf
, 8)) {
164 afm
->FullName
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
168 if(!strncmp("FamilyName", buf
, 10)) {
169 afm
->FamilyName
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
173 if(!strncmp("Weight", buf
, 6)) {
174 if(!strncmp("Roman", value
, 5) || !strncmp("Medium", value
, 6)
175 || !strncmp("Book", value
, 4) || !strncmp("Regular", value
, 7)
176 || !strncmp("Normal", value
, 6))
177 afm
->Weight
= FW_NORMAL
;
178 else if(!strncmp("Demi", value
, 4))
179 afm
->Weight
= FW_DEMIBOLD
;
180 else if(!strncmp("Bold", value
, 4))
181 afm
->Weight
= FW_BOLD
;
182 else if(!strncmp("Light", value
, 5))
183 afm
->Weight
= FW_LIGHT
;
184 else if(!strncmp("Black", value
, 5))
185 afm
->Weight
= FW_BLACK
;
187 FIXME("Unkown AFM Weight '%s'\n", value
);
188 afm
->Weight
= FW_NORMAL
;
193 if(!strncmp("ItalicAngle", buf
, 11)) {
194 sscanf(value
, "%f", &(afm
->ItalicAngle
));
198 if(!strncmp("IsFixedPitch", buf
, 12)) {
199 if(!strncasecmp("false", value
, 5))
200 afm
->IsFixedPitch
= FALSE
;
202 afm
->IsFixedPitch
= TRUE
;
206 if(!strncmp("FontBBox", buf
, 8)) {
207 sscanf(value
, "%f %f %f %f", &(afm
->FontBBox
.llx
),
208 &(afm
->FontBBox
.lly
), &(afm
->FontBBox
.urx
),
209 &(afm
->FontBBox
.ury
) );
213 if(!strncmp("UnderlinePosition", buf
, 17)) {
214 sscanf(value
, "%f", &(afm
->UnderlinePosition
) );
218 if(!strncmp("UnderlineThickness", buf
, 18)) {
219 sscanf(value
, "%f", &(afm
->UnderlineThickness
) );
223 if(!strncmp("CapHeight", buf
, 9)) {
224 sscanf(value
, "%f", &(afm
->CapHeight
) );
228 if(!strncmp("XHeight", buf
, 7)) {
229 sscanf(value
, "%f", &(afm
->XHeight
) );
233 if(!strncmp("Ascender", buf
, 8)) {
234 sscanf(value
, "%f", &(afm
->Ascender
) );
238 if(!strncmp("Descender", buf
, 9)) {
239 sscanf(value
, "%f", &(afm
->Descender
) );
243 if(!strncmp("StartCharMetrics", buf
, 16)) {
244 sscanf(value
, "%d", &(afm
->NumofMetrics
) );
245 PSDRV_AFMGetCharMetrics(afm
, fp
);
249 if(!strncmp("EncodingScheme", buf
, 14)) {
250 afm
->EncodingScheme
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
257 if(afm
->FontName
== NULL
)
258 WARN("%s contains no FontName.\n", file
);
259 if(afm
->FullName
== NULL
)
260 afm
->FullName
= HEAP_strdupA(PSDRV_Heap
, 0, afm
->FontName
);
261 if(afm
->FamilyName
== NULL
)
262 afm
->FamilyName
= HEAP_strdupA(PSDRV_Heap
, 0, afm
->FontName
);
263 if(afm
->Ascender
== 0.0)
264 afm
->Ascender
= afm
->FontBBox
.ury
;
265 if(afm
->Descender
== 0.0)
266 afm
->Descender
= afm
->FontBBox
.lly
;
267 if(afm
->FullAscender
== 0.0)
268 afm
->FullAscender
= afm
->Ascender
;
270 afm
->Weight
= FW_NORMAL
;
275 /***********************************************************
279 * Frees the family and afmlistentry structures in list head
281 void PSDRV_FreeAFMList( FONTFAMILY
*head
)
283 AFMLISTENTRY
*afmle
, *nexta
;
284 FONTFAMILY
*family
, *nextf
;
286 for(nextf
= family
= head
; nextf
; family
= nextf
) {
287 for(nexta
= afmle
= family
->afmlist
; nexta
; afmle
= nexta
) {
289 HeapFree( PSDRV_Heap
, 0, afmle
);
291 nextf
= family
->next
;
292 HeapFree( PSDRV_Heap
, 0, family
);
298 /***********************************************************
300 * PSDRV_FindAFMinList
301 * Returns ptr to an AFM if name (which is a PS font name) exists in list
304 AFM
*PSDRV_FindAFMinList(FONTFAMILY
*head
, char *name
)
309 for(family
= head
; family
; family
= family
->next
) {
310 for(afmle
= family
->afmlist
; afmle
; afmle
= afmle
->next
) {
311 if(!strcmp(afmle
->afm
->FontName
, name
))
318 /***********************************************************
322 * Adds an afm to the list whose head is pointed to by head. Creates new
323 * family node if necessary and always creates a new AFMLISTENTRY.
325 void PSDRV_AddAFMtoList(FONTFAMILY
**head
, AFM
*afm
)
327 FONTFAMILY
*family
= *head
;
328 FONTFAMILY
**insert
= head
;
329 AFMLISTENTRY
*tmpafmle
, *newafmle
;
331 newafmle
= HeapAlloc(PSDRV_Heap
, HEAP_ZERO_MEMORY
,
336 if(!strcmp(family
->FamilyName
, afm
->FamilyName
))
338 insert
= &(family
->next
);
339 family
= family
->next
;
343 family
= HeapAlloc(PSDRV_Heap
, HEAP_ZERO_MEMORY
,
346 family
->FamilyName
= HEAP_strdupA(PSDRV_Heap
, 0,
348 family
->afmlist
= newafmle
;
352 tmpafmle
= family
->afmlist
;
353 while(tmpafmle
->next
)
354 tmpafmle
= tmpafmle
->next
;
356 tmpafmle
->next
= newafmle
;
361 /**********************************************************
363 * PSDRV_ReencodeCharWidths
365 * Re map the CharWidths field of the afm to correspond to an ANSI encoding
368 static void PSDRV_ReencodeCharWidths(AFM
*afm
)
373 for(i
= 0; i
< 256; i
++) {
376 if(PSDRV_ANSIVector
[i
] == NULL
) {
377 afm
->CharWidths
[i
] = 0.0;
380 for(metric
= afm
->Metrics
; metric
; metric
= metric
->next
) {
381 if(!strcmp(metric
->N
, PSDRV_ANSIVector
[i
])) {
382 afm
->CharWidths
[i
] = metric
->WX
;
387 WARN("Couldn't find glyph '%s' in font '%s'\n",
388 PSDRV_ANSIVector
[i
], afm
->FontName
);
389 afm
->CharWidths
[i
] = 0.0;
396 /***********************************************************
401 static void PSDRV_DumpFontList(void)
406 for(family
= PSDRV_AFMFontList
; family
; family
= family
->next
) {
407 TRACE("Family '%s'\n", family
->FamilyName
);
408 for(afmle
= family
->afmlist
; afmle
; afmle
= afmle
->next
) {
409 TRACE("\tFontName '%s'\n", afmle
->afm
->FontName
);
416 /***********************************************************
418 * PSDRV_GetFontMetrics
420 * Only exported function in this file. Parses all afm files listed in
421 * [afmfiles] of wine.conf .
423 BOOL
PSDRV_GetFontMetrics(void)
429 while (PROFILE_EnumWineIniString( "afmfiles", idx
++, key
, sizeof(key
), value
, sizeof(value
)))
431 AFM
*afm
= PSDRV_AFMParse(value
);
434 if(afm
->EncodingScheme
&&
435 !strcmp(afm
->EncodingScheme
, "AdobeStandardEncoding")) {
436 PSDRV_ReencodeCharWidths(afm
);
438 PSDRV_AddAFMtoList(&PSDRV_AFMFontList
, afm
);
441 PSDRV_DumpFontList();