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 <limits.h> /* INT_MIN */
14 #include <float.h> /* FLT_MAX */
15 #include "winnt.h" /* HEAP_ZERO_MEMORY */
18 #include "debugtools.h"
21 DEFAULT_DEBUG_CHANNEL(psdrv
);
24 /* ptr to fonts for which we have afm files */
25 FONTFAMILY
*PSDRV_AFMFontList
= NULL
;
27 /*******************************************************************************
30 * Check an AFMMETRICS structure to make sure all elements have been properly
34 static const AFMMETRICS badMetrics
=
39 { FLT_MAX
, FLT_MAX
, FLT_MAX
, FLT_MAX
}, /* B */
43 inline static BOOL
CheckMetrics(const AFMMETRICS
*metrics
)
45 if ( metrics
->C
== badMetrics
.C
||
46 metrics
->WX
== badMetrics
.WX
||
47 metrics
->N
== badMetrics
.N
||
48 metrics
->B
.llx
== badMetrics
.B
.llx
||
49 metrics
->B
.lly
== badMetrics
.B
.lly
||
50 metrics
->B
.urx
== badMetrics
.B
.urx
||
51 metrics
->B
.ury
== badMetrics
.B
.ury
)
57 /*******************************************************************************
60 * Free an AFM structure and any subsidiary objects that have been allocated
63 static void FreeAFM(AFM
*afm
)
65 if (afm
->FontName
!= NULL
)
66 HeapFree(PSDRV_Heap
, 0, afm
->FontName
);
67 if (afm
->FullName
!= NULL
)
68 HeapFree(PSDRV_Heap
, 0, afm
->FullName
);
69 if (afm
->FamilyName
!= NULL
)
70 HeapFree(PSDRV_Heap
, 0, afm
->FamilyName
);
71 if (afm
->EncodingScheme
!= NULL
)
72 HeapFree(PSDRV_Heap
, 0, afm
->EncodingScheme
);
73 if (afm
->Metrics
!= NULL
)
74 HeapFree(PSDRV_Heap
, 0, afm
->Metrics
);
76 HeapFree(PSDRV_Heap
, 0, afm
);
80 /***********************************************************
82 * PSDRV_AFMGetCharMetrics
84 * Parses CharMetric section of AFM file.
86 * Actually only collects the widths of numbered chars and puts then in
89 static BOOL
PSDRV_AFMGetCharMetrics(AFM
*afm
, FILE *fp
)
91 unsigned char line
[256], valbuf
[256];
92 unsigned char *cp
, *item
, *value
, *curpos
, *endpos
;
96 afm
->Metrics
= metric
= HeapAlloc( PSDRV_Heap
, 0,
97 afm
->NumofMetrics
* sizeof(AFMMETRICS
) );
101 for(i
= 0; i
< afm
->NumofMetrics
; i
++, metric
++) {
103 *metric
= badMetrics
;
106 if(!fgets(line
, sizeof(line
), fp
)) {
107 ERR("Unexpected EOF\n");
108 HeapFree(PSDRV_Heap
, 0, afm
->Metrics
);
112 cp
= line
+ strlen(line
);
116 } while(cp
>= line
&& isspace(*cp
));
122 while(isspace(*item
))
124 value
= strpbrk(item
, " \t");
126 ERR("No whitespace found.\n");
127 HeapFree(PSDRV_Heap
, 0, afm
->Metrics
);
131 while(isspace(*value
))
133 cp
= endpos
= strchr(value
, ';');
135 ERR("missing ;, failed. [%s]\n", line
);
136 HeapFree(PSDRV_Heap
, 0, afm
->Metrics
);
140 while(isspace(*--cp
))
142 memcpy(valbuf
, value
, cp
- value
+ 1);
143 valbuf
[cp
- value
+ 1] = '\0';
146 if(!strncmp(item
, "C ", 2)) {
147 value
= strchr(item
, ' ');
148 sscanf(value
, " %d", &metric
->C
);
150 } else if(!strncmp(item
, "CH ", 3)) {
151 value
= strrchr(item
, ' ');
152 sscanf(value
, " %x", &metric
->C
);
155 else if(!strncmp("WX ", item
, 3) || !strncmp("W0X ", item
, 4)) {
156 sscanf(value
, "%f", &metric
->WX
);
157 if(metric
->C
>= 0 && metric
->C
<= 0xff)
158 afm
->CharWidths
[metric
->C
] = metric
->WX
;
161 else if(!strncmp("N ", item
, 2)) {
162 metric
->N
= PSDRV_GlyphName(value
);
165 else if(!strncmp("B ", item
, 2)) {
166 sscanf(value
, "%f%f%f%f", &metric
->B
.llx
, &metric
->B
.lly
,
167 &metric
->B
.urx
, &metric
->B
.ury
);
169 /* Store height of Aring to use as lfHeight */
170 if(metric
->N
&& !strncmp(metric
->N
->sz
, "Aring", 5))
171 afm
->FullAscender
= metric
->B
.ury
;
174 /* Ligatures go here... */
179 if (CheckMetrics(metric
) == FALSE
) {
180 ERR("Error parsing character metrics\n");
181 HeapFree(PSDRV_Heap
, 0, afm
->Metrics
);
186 TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n",
187 metric
->N
->sz
, metric
->WX
, metric
->B
.llx
, metric
->B
.lly
,
188 metric
->B
.urx
, metric
->B
.ury
);
194 /***********************************************************
198 * Fills out an AFM structure and associated substructures (see psdrv.h)
199 * for a given AFM file. All memory is allocated from the process heap.
200 * Returns a ptr to the AFM structure or NULL on error.
202 * This is not complete (we don't handle kerning yet) and not efficient
205 static AFM
*PSDRV_AFMParse(char const *file
)
208 unsigned char buf
[256];
209 unsigned char *value
;
215 TRACE("parsing '%s'\n", file
);
217 if((fp
= fopen(file
, "r")) == NULL
) {
218 MESSAGE("Can't open AFM file '%s'. Please check wine.conf .\n", file
);
222 afm
= HeapAlloc(PSDRV_Heap
, HEAP_ZERO_MEMORY
, sizeof(AFM
));
229 while ( ( c
= fgetc ( fp
) ) != EOF
) {
231 if ( *cp
== '\r' || *cp
== '\n' || cp
- buf
== sizeof(buf
)-2 ) {
241 cp
= buf
+ strlen(buf
);
245 } while(cp
> buf
&& isspace(*cp
));
249 if ( afmfile
== 0 && strncmp ( buf
, "StartFontMetrics", 16 ) )
253 value
= strchr(buf
, ' ');
255 while(isspace(*value
))
258 if(!strncmp("FontName", buf
, 8)) {
259 afm
->FontName
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
260 if (afm
->FontName
== NULL
) {
268 if(!strncmp("FullName", buf
, 8)) {
269 afm
->FullName
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
270 if (afm
->FullName
== NULL
) {
278 if(!strncmp("FamilyName", buf
, 10)) {
279 afm
->FamilyName
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
280 if (afm
->FamilyName
== NULL
) {
288 if(!strncmp("Weight", buf
, 6)) {
289 if(!strncmp("Roman", value
, 5) || !strncmp("Medium", value
, 6)
290 || !strncmp("Book", value
, 4) || !strncmp("Regular", value
, 7)
291 || !strncmp("Normal", value
, 6))
292 afm
->Weight
= FW_NORMAL
;
293 else if(!strncmp("Demi", value
, 4))
294 afm
->Weight
= FW_DEMIBOLD
;
295 else if(!strncmp("Bold", value
, 4))
296 afm
->Weight
= FW_BOLD
;
297 else if(!strncmp("Light", value
, 5))
298 afm
->Weight
= FW_LIGHT
;
299 else if(!strncmp("Black", value
, 5))
300 afm
->Weight
= FW_BLACK
;
302 WARN("%s specifies unknown Weight '%s'; treating as Roman\n",
304 afm
->Weight
= FW_NORMAL
;
309 if(!strncmp("ItalicAngle", buf
, 11)) {
310 sscanf(value
, "%f", &(afm
->ItalicAngle
));
314 if(!strncmp("IsFixedPitch", buf
, 12)) {
315 if(!strncasecmp("false", value
, 5))
316 afm
->IsFixedPitch
= FALSE
;
318 afm
->IsFixedPitch
= TRUE
;
322 if(!strncmp("FontBBox", buf
, 8)) {
323 sscanf(value
, "%f %f %f %f", &(afm
->FontBBox
.llx
),
324 &(afm
->FontBBox
.lly
), &(afm
->FontBBox
.urx
),
325 &(afm
->FontBBox
.ury
) );
329 if(!strncmp("UnderlinePosition", buf
, 17)) {
330 sscanf(value
, "%f", &(afm
->UnderlinePosition
) );
334 if(!strncmp("UnderlineThickness", buf
, 18)) {
335 sscanf(value
, "%f", &(afm
->UnderlineThickness
) );
339 if(!strncmp("CapHeight", buf
, 9)) {
340 sscanf(value
, "%f", &(afm
->CapHeight
) );
344 if(!strncmp("XHeight", buf
, 7)) {
345 sscanf(value
, "%f", &(afm
->XHeight
) );
349 if(!strncmp("Ascender", buf
, 8)) {
350 sscanf(value
, "%f", &(afm
->Ascender
) );
354 if(!strncmp("Descender", buf
, 9)) {
355 sscanf(value
, "%f", &(afm
->Descender
) );
359 if(!strncmp("StartCharMetrics", buf
, 16)) {
360 sscanf(value
, "%d", &(afm
->NumofMetrics
) );
361 if (PSDRV_AFMGetCharMetrics(afm
, fp
) == FALSE
) {
369 if(!strncmp("EncodingScheme", buf
, 14)) {
370 afm
->EncodingScheme
= HEAP_strdupA(PSDRV_Heap
, 0, value
);
371 if (afm
->EncodingScheme
== NULL
) {
383 HeapFree ( PSDRV_Heap
, 0, afm
);
387 if(afm
->FontName
== NULL
) {
388 WARN("%s contains no FontName.\n", file
);
389 afm
->FontName
= HEAP_strdupA(PSDRV_Heap
, 0, "nofont");
390 if (afm
->FontName
== NULL
) {
396 if(afm
->FullName
== NULL
)
397 afm
->FullName
= HEAP_strdupA(PSDRV_Heap
, 0, afm
->FontName
);
398 if(afm
->FamilyName
== NULL
)
399 afm
->FamilyName
= HEAP_strdupA(PSDRV_Heap
, 0, afm
->FontName
);
400 if (afm
->FullName
== NULL
|| afm
->FamilyName
== NULL
) {
405 if(afm
->Ascender
== 0.0)
406 afm
->Ascender
= afm
->FontBBox
.ury
;
407 if(afm
->Descender
== 0.0)
408 afm
->Descender
= afm
->FontBBox
.lly
;
409 if(afm
->FullAscender
== 0.0)
410 afm
->FullAscender
= afm
->Ascender
;
412 afm
->Weight
= FW_NORMAL
;
417 /***********************************************************
421 * Frees the family and afmlistentry structures in list head
423 void PSDRV_FreeAFMList( FONTFAMILY
*head
)
425 AFMLISTENTRY
*afmle
, *nexta
;
426 FONTFAMILY
*family
, *nextf
;
428 for(nextf
= family
= head
; nextf
; family
= nextf
) {
429 for(nexta
= afmle
= family
->afmlist
; nexta
; afmle
= nexta
) {
431 HeapFree( PSDRV_Heap
, 0, afmle
);
433 nextf
= family
->next
;
434 HeapFree( PSDRV_Heap
, 0, family
);
440 /***********************************************************
442 * PSDRV_FindAFMinList
443 * Returns ptr to an AFM if name (which is a PS font name) exists in list
446 AFM
*PSDRV_FindAFMinList(FONTFAMILY
*head
, char *name
)
451 for(family
= head
; family
; family
= family
->next
) {
452 for(afmle
= family
->afmlist
; afmle
; afmle
= afmle
->next
) {
453 if(!strcmp(afmle
->afm
->FontName
, name
))
460 /***********************************************************
464 * Adds an afm to the list whose head is pointed to by head. Creates new
465 * family node if necessary and always creates a new AFMLISTENTRY.
467 BOOL
PSDRV_AddAFMtoList(FONTFAMILY
**head
, AFM
*afm
)
469 FONTFAMILY
*family
= *head
;
470 FONTFAMILY
**insert
= head
;
471 AFMLISTENTRY
*tmpafmle
, *newafmle
;
473 newafmle
= HeapAlloc(PSDRV_Heap
, HEAP_ZERO_MEMORY
,
475 if (newafmle
== NULL
)
481 if(!strcmp(family
->FamilyName
, afm
->FamilyName
))
483 insert
= &(family
->next
);
484 family
= family
->next
;
488 family
= HeapAlloc(PSDRV_Heap
, HEAP_ZERO_MEMORY
,
490 if (family
== NULL
) {
491 HeapFree(PSDRV_Heap
, 0, newafmle
);
495 family
->FamilyName
= HEAP_strdupA(PSDRV_Heap
, 0,
497 if (family
->FamilyName
== NULL
) {
498 HeapFree(PSDRV_Heap
, 0, family
);
499 HeapFree(PSDRV_Heap
, 0, newafmle
);
502 family
->afmlist
= newafmle
;
506 tmpafmle
= family
->afmlist
;
507 while(tmpafmle
->next
)
508 tmpafmle
= tmpafmle
->next
;
510 tmpafmle
->next
= newafmle
;
515 /**********************************************************
517 * PSDRV_ReencodeCharWidths
519 * Re map the CharWidths field of the afm to correspond to an ANSI encoding
522 static void PSDRV_ReencodeCharWidths(AFM
*afm
)
527 for(i
= 0; i
< 256; i
++) {
530 if(PSDRV_ANSIVector
[i
] == NULL
) {
531 afm
->CharWidths
[i
] = 0.0;
534 for (j
= 0, metric
= afm
->Metrics
; j
< afm
->NumofMetrics
; j
++, metric
++) {
535 if(metric
->N
&& !strcmp(metric
->N
->sz
, PSDRV_ANSIVector
[i
])) {
536 afm
->CharWidths
[i
] = metric
->WX
;
540 if(j
== afm
->NumofMetrics
) {
541 WARN("Couldn't find glyph '%s' in font '%s'\n",
542 PSDRV_ANSIVector
[i
], afm
->FontName
);
543 afm
->CharWidths
[i
] = 0.0;
550 /***********************************************************
555 static void PSDRV_DumpFontList(void)
560 for(family
= PSDRV_AFMFontList
; family
; family
= family
->next
) {
561 TRACE("Family '%s'\n", family
->FamilyName
);
562 for(afmle
= family
->afmlist
; afmle
; afmle
= afmle
->next
) {
563 TRACE("\tFontName '%s'\n", afmle
->afm
->FontName
);
570 /***********************************************************
572 * PSDRV_GetFontMetrics
574 * Parses all afm files listed in [afmfiles] and [afmdirs] of wine.conf
576 * If this function fails, PSDRV_Init will destroy PSDRV_Heap, so don't worry
577 * about freeing all the memory that's been allocated.
580 static BOOL
PSDRV_ReadAFMDir(const char* afmdir
) {
584 dir
= opendir(afmdir
);
587 while ((dent
=readdir(dir
))) {
588 if (strstr(dent
->d_name
,".afm")) {
591 afmfn
=(char*)HeapAlloc(PSDRV_Heap
,0,
592 strlen(afmdir
)+strlen(dent
->d_name
)+2);
597 strcpy(afmfn
,afmdir
);
599 strcat(afmfn
,dent
->d_name
);
600 TRACE("loading AFM %s\n",afmfn
);
601 afm
= PSDRV_AFMParse(afmfn
);
603 if(afm
->EncodingScheme
&&
604 !strcmp(afm
->EncodingScheme
,"AdobeStandardEncoding")) {
605 PSDRV_ReencodeCharWidths(afm
);
607 if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList
, afm
) == FALSE
) {
614 WARN("Error parsing %s\n", afmfn
);
616 HeapFree(PSDRV_Heap
,0,afmfn
);
622 WARN("Error opening %s\n", afmdir
);
628 BOOL
PSDRV_GetFontMetrics(void)
634 if (PSDRV_GlyphListInit() != 0)
637 while (PROFILE_EnumWineIniString( "afmfiles", idx
++, key
, sizeof(key
),
638 value
, sizeof(value
)))
640 AFM
* afm
= PSDRV_AFMParse(value
);
643 if(afm
->EncodingScheme
&&
644 !strcmp(afm
->EncodingScheme
, "AdobeStandardEncoding")) {
645 PSDRV_ReencodeCharWidths(afm
);
647 if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList
, afm
) == FALSE
) {
652 WARN("Error parsing %s\n", value
);
656 for (idx
= 0; PROFILE_EnumWineIniString ("afmdirs", idx
, key
, sizeof (key
),
657 value
, sizeof (value
)); ++idx
)
658 if (PSDRV_ReadAFMDir (value
) == FALSE
)
661 PSDRV_DumpGlyphList();
662 PSDRV_DumpFontList();