2 * Adobe Font Metric (AFM) file parsing
3 * See http://partners.adobe.com/asn/developer/PDFS/TN/5004.AFM_Spec.pdf
5 * Copyright 1998 Huw D M Davies
13 #include "winnt.h" /* HEAP_ZERO_MEMORY */
16 #include "debugtools.h"
19 DEFAULT_DEBUG_CHANNEL(psdrv
);
22 /* ptr to fonts for which we have afm files */
23 FONTFAMILY
*PSDRV_AFMFontList
= NULL
;
26 /***********************************************************
28 * PSDRV_AFMGetCharMetrics
30 * Parses CharMetric section of AFM file.
32 * Actually only collects the widths of numbered chars and puts then in
35 static void PSDRV_AFMGetCharMetrics(AFM
*afm
, FILE *fp
)
37 char line
[256], valbuf
[256];
38 char *cp
, *item
, *value
, *curpos
, *endpos
;
42 afm
->Metrics
= metric
= HeapAlloc( PSDRV_Heap
, HEAP_ZERO_MEMORY
,
43 afm
->NumofMetrics
* sizeof(AFMMETRICS
) );
44 for(i
= 0; i
< afm
->NumofMetrics
; i
++, metric
++) {
46 if(!fgets(line
, sizeof(line
), fp
)) {
47 ERR("Unexpected EOF\n");
50 cp
= line
+ strlen(line
);
54 } while(cp
> line
&& isspace(*cp
));
61 value
= strpbrk(item
, " \t");
62 if (!value
) { ERR("No whitespace found.\n");return;}
63 while(isspace(*value
))
65 cp
= endpos
= strchr(value
, ';');
66 if (!cp
) { ERR("missing ;, failed.\n"); return; }
69 memcpy(valbuf
, value
, cp
- value
+ 1);
70 valbuf
[cp
- value
+ 1] = '\0';
73 if(!strncmp(item
, "C ", 2)) {
74 value
= strchr(item
, ' ');
75 sscanf(value
, " %d", &metric
->C
);
77 } else if(!strncmp(item
, "CH ", 3)) {
78 value
= strrchr(item
, ' ');
79 sscanf(value
, " %x", &metric
->C
);
82 else if(!strncmp("WX ", item
, 3) || !strncmp("W0X ", item
, 4)) {
83 sscanf(value
, "%f", &metric
->WX
);
84 if(metric
->C
>= 0 && metric
->C
<= 0xff)
85 afm
->CharWidths
[metric
->C
] = metric
->WX
;
88 else if(!strncmp("N ", item
, 2)) {
89 strncpy( metric
->N
, value
, sizeof(metric
->N
) );
92 else if(!strncmp("B ", item
, 2)) {
93 sscanf(value
, "%f%f%f%f", &metric
->B
.llx
, &metric
->B
.lly
,
94 &metric
->B
.urx
, &metric
->B
.ury
);
96 /* Store height of Aring to use as lfHeight */
97 if(metric
->N
&& !strncmp(metric
->N
, "Aring", 5))
98 afm
->FullAscender
= metric
->B
.ury
;
101 /* Ligatures go here... */
107 TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n",
108 metric
->N
, metric
->WX
, metric
->B
.llx
, metric
->B
.lly
,
109 metric
->B
.urx
, metric
->B
.ury
);
116 /***********************************************************
120 * Fills out an AFM structure and associated substructures (see psdrv.h)
121 * for a given AFM file. All memory is allocated from the process heap.
122 * Returns a ptr to the AFM structure or NULL on error.
124 * This is not complete (we don't handle kerning yet) and not efficient
126 static AFM
*PSDRV_AFMParse(char const *file
)
134 TRACE("parsing '%s'\n", file
);
136 if((fp
= fopen(file
, "r")) == NULL
) {
137 MESSAGE("Can't open AFM file '%s'. Please check wine.conf .\n", file
);
141 afm
= HeapAlloc(PSDRV_Heap
, HEAP_ZERO_MEMORY
, sizeof(AFM
));
147 while(fgets(buf
, sizeof(buf
), fp
)) {
148 cp
= buf
+ strlen(buf
);
152 } while(cp
> buf
&& isspace(*cp
));
154 value
= strchr(buf
, ' ');
156 while(isspace(*value
))
159 if(!strncmp("FontName", buf
, 8)) {
160 afm
->FontName
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
164 if(!strncmp("FullName", buf
, 8)) {
165 afm
->FullName
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
169 if(!strncmp("FamilyName", buf
, 10)) {
170 afm
->FamilyName
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
174 if(!strncmp("Weight", buf
, 6)) {
175 if(!strncmp("Roman", value
, 5) || !strncmp("Medium", value
, 6)
176 || !strncmp("Book", value
, 4) || !strncmp("Regular", value
, 7)
177 || !strncmp("Normal", value
, 6))
178 afm
->Weight
= FW_NORMAL
;
179 else if(!strncmp("Demi", value
, 4))
180 afm
->Weight
= FW_DEMIBOLD
;
181 else if(!strncmp("Bold", value
, 4))
182 afm
->Weight
= FW_BOLD
;
183 else if(!strncmp("Light", value
, 5))
184 afm
->Weight
= FW_LIGHT
;
185 else if(!strncmp("Black", value
, 5))
186 afm
->Weight
= FW_BLACK
;
188 FIXME("%s: Unkown AFM Weight '%s'\n", file
,value
);
189 afm
->Weight
= FW_NORMAL
;
194 if(!strncmp("ItalicAngle", buf
, 11)) {
195 sscanf(value
, "%f", &(afm
->ItalicAngle
));
199 if(!strncmp("IsFixedPitch", buf
, 12)) {
200 if(!strncasecmp("false", value
, 5))
201 afm
->IsFixedPitch
= FALSE
;
203 afm
->IsFixedPitch
= TRUE
;
207 if(!strncmp("FontBBox", buf
, 8)) {
208 sscanf(value
, "%f %f %f %f", &(afm
->FontBBox
.llx
),
209 &(afm
->FontBBox
.lly
), &(afm
->FontBBox
.urx
),
210 &(afm
->FontBBox
.ury
) );
214 if(!strncmp("UnderlinePosition", buf
, 17)) {
215 sscanf(value
, "%f", &(afm
->UnderlinePosition
) );
219 if(!strncmp("UnderlineThickness", buf
, 18)) {
220 sscanf(value
, "%f", &(afm
->UnderlineThickness
) );
224 if(!strncmp("CapHeight", buf
, 9)) {
225 sscanf(value
, "%f", &(afm
->CapHeight
) );
229 if(!strncmp("XHeight", buf
, 7)) {
230 sscanf(value
, "%f", &(afm
->XHeight
) );
234 if(!strncmp("Ascender", buf
, 8)) {
235 sscanf(value
, "%f", &(afm
->Ascender
) );
239 if(!strncmp("Descender", buf
, 9)) {
240 sscanf(value
, "%f", &(afm
->Descender
) );
244 if(!strncmp("StartCharMetrics", buf
, 16)) {
245 sscanf(value
, "%d", &(afm
->NumofMetrics
) );
246 PSDRV_AFMGetCharMetrics(afm
, fp
);
250 if(!strncmp("EncodingScheme", buf
, 14)) {
251 afm
->EncodingScheme
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
258 if(afm
->FontName
== NULL
)
259 WARN("%s contains no FontName.\n", file
);
260 if(afm
->FullName
== NULL
)
261 afm
->FullName
= HEAP_strdupA(PSDRV_Heap
, 0, afm
->FontName
);
262 if(afm
->FamilyName
== NULL
)
263 afm
->FamilyName
= HEAP_strdupA(PSDRV_Heap
, 0, afm
->FontName
);
264 if(afm
->Ascender
== 0.0)
265 afm
->Ascender
= afm
->FontBBox
.ury
;
266 if(afm
->Descender
== 0.0)
267 afm
->Descender
= afm
->FontBBox
.lly
;
268 if(afm
->FullAscender
== 0.0)
269 afm
->FullAscender
= afm
->Ascender
;
271 afm
->Weight
= FW_NORMAL
;
276 /***********************************************************
280 * Frees the family and afmlistentry structures in list head
282 void PSDRV_FreeAFMList( FONTFAMILY
*head
)
284 AFMLISTENTRY
*afmle
, *nexta
;
285 FONTFAMILY
*family
, *nextf
;
287 for(nextf
= family
= head
; nextf
; family
= nextf
) {
288 for(nexta
= afmle
= family
->afmlist
; nexta
; afmle
= nexta
) {
290 HeapFree( PSDRV_Heap
, 0, afmle
);
292 nextf
= family
->next
;
293 HeapFree( PSDRV_Heap
, 0, family
);
299 /***********************************************************
301 * PSDRV_FindAFMinList
302 * Returns ptr to an AFM if name (which is a PS font name) exists in list
305 AFM
*PSDRV_FindAFMinList(FONTFAMILY
*head
, char *name
)
310 for(family
= head
; family
; family
= family
->next
) {
311 for(afmle
= family
->afmlist
; afmle
; afmle
= afmle
->next
) {
312 if(!strcmp(afmle
->afm
->FontName
, name
))
319 /***********************************************************
323 * Adds an afm to the list whose head is pointed to by head. Creates new
324 * family node if necessary and always creates a new AFMLISTENTRY.
326 void PSDRV_AddAFMtoList(FONTFAMILY
**head
, AFM
*afm
)
328 FONTFAMILY
*family
= *head
;
329 FONTFAMILY
**insert
= head
;
330 AFMLISTENTRY
*tmpafmle
, *newafmle
;
332 newafmle
= HeapAlloc(PSDRV_Heap
, HEAP_ZERO_MEMORY
,
337 if(!strcmp(family
->FamilyName
, afm
->FamilyName
))
339 insert
= &(family
->next
);
340 family
= family
->next
;
344 family
= HeapAlloc(PSDRV_Heap
, HEAP_ZERO_MEMORY
,
347 family
->FamilyName
= HEAP_strdupA(PSDRV_Heap
, 0,
349 family
->afmlist
= newafmle
;
353 tmpafmle
= family
->afmlist
;
354 while(tmpafmle
->next
)
355 tmpafmle
= tmpafmle
->next
;
357 tmpafmle
->next
= newafmle
;
362 /**********************************************************
364 * PSDRV_ReencodeCharWidths
366 * Re map the CharWidths field of the afm to correspond to an ANSI encoding
369 static void PSDRV_ReencodeCharWidths(AFM
*afm
)
374 for(i
= 0; i
< 256; i
++) {
377 if(PSDRV_ANSIVector
[i
] == NULL
) {
378 afm
->CharWidths
[i
] = 0.0;
381 for (j
= 0, metric
= afm
->Metrics
; j
< afm
->NumofMetrics
; j
++, metric
++) {
382 if(!strcmp(metric
->N
, PSDRV_ANSIVector
[i
])) {
383 afm
->CharWidths
[i
] = metric
->WX
;
387 if(j
== afm
->NumofMetrics
) {
388 WARN("Couldn't find glyph '%s' in font '%s'\n",
389 PSDRV_ANSIVector
[i
], afm
->FontName
);
390 afm
->CharWidths
[i
] = 0.0;
397 /***********************************************************
402 static void PSDRV_DumpFontList(void)
407 for(family
= PSDRV_AFMFontList
; family
; family
= family
->next
) {
408 TRACE("Family '%s'\n", family
->FamilyName
);
409 for(afmle
= family
->afmlist
; afmle
; afmle
= afmle
->next
) {
410 TRACE("\tFontName '%s'\n", afmle
->afm
->FontName
);
417 /***********************************************************
419 * PSDRV_GetFontMetrics
421 * Only exported function in this file. Parses all afm files listed in
422 * [afmfiles] of wine.conf .
425 static void PSDRV_ReadAFMDir(const char* afmdir
) {
429 dir
= opendir(afmdir
);
432 while ((dent
=readdir(dir
))) {
433 if (strstr(dent
->d_name
,".afm")) {
436 afmfn
=(char*)HeapAlloc(GetProcessHeap(),0,strlen(afmdir
)+strlen(dent
->d_name
)+1);
437 strcpy(afmfn
,afmdir
);
438 strcat(afmfn
,dent
->d_name
);
439 TRACE("loading AFM %s\n",afmfn
);
440 afm
= PSDRV_AFMParse(afmfn
);
442 if(afm
->EncodingScheme
&&
443 !strcmp(afm
->EncodingScheme
,"AdobeStandardEncoding")) {
444 PSDRV_ReencodeCharWidths(afm
);
446 PSDRV_AddAFMtoList(&PSDRV_AFMFontList
, afm
);
448 HeapFree(GetProcessHeap(),0,afmfn
);
455 BOOL
PSDRV_GetFontMetrics(void)
461 /* some packages with afm files in that directory */
462 PSDRV_ReadAFMDir("/usr/share/ghostscript/fonts/");
463 PSDRV_ReadAFMDir("/usr/share/a2ps/afm/");
464 PSDRV_ReadAFMDir("/usr/share/enscript/");
465 PSDRV_ReadAFMDir("/usr/X11R6/lib/X11/fonts/Type1/");
470 /* this takes rather long to load :/ */
471 /* teTeX has a 3level directory storage of afm files */
472 char *path
="/opt/teTeX/share/texmf/fonts/afm/";
473 DIR *dir
= opendir(path
);
477 while ((dent
=readdir(dir
))) {
481 if (dent
->d_name
[0]=='.')
483 path2
=(char*)HeapAlloc(GetProcessHeap(),0,strlen(path
)+1+1+strlen(dent
->d_name
));
485 strcat(path2
,dent
->d_name
);
487 dir2
= opendir(path2
);
489 while ((dent
=readdir(dir2
))) {
491 if (dent
->d_name
[0]=='.')
493 path3
=(char*)HeapAlloc(GetProcessHeap(),0,strlen(path2
)+1+1+strlen(dent
->d_name
));
495 strcat(path3
,dent
->d_name
);
497 PSDRV_ReadAFMDir(path3
);
498 HeapFree(GetProcessHeap(),0,path3
);
502 PSDRV_ReadAFMDir(path2
);
503 HeapFree(GetProcessHeap(),0,path2
);
510 while (PROFILE_EnumWineIniString( "afmfiles", idx
++, key
, sizeof(key
), value
, sizeof(value
)))
512 AFM
* afm
= PSDRV_AFMParse(value
);
515 if(afm
->EncodingScheme
&&
516 !strcmp(afm
->EncodingScheme
, "AdobeStandardEncoding")) {
517 PSDRV_ReencodeCharWidths(afm
);
519 PSDRV_AddAFMtoList(&PSDRV_AFMFontList
, afm
);
522 PSDRV_DumpFontList();