First support of DGA 2.0 for DirectDraw.
[wine] / graphics / psdrv / afm.c
1 /*
2  *      Adobe Font Metric (AFM) file parsing
3  *      See http://www.adobe.com/supportservice/devrelations/PDFS/TN/5004.AFM_Spec.pdf
4  *
5  *      Copyright 1998  Huw D M Davies
6  * 
7  */
8
9 #include <string.h>
10 #include "winnt.h" /* HEAP_ZERO_MEMORY */
11 #include "psdrv.h"
12 #include "options.h"
13 #include "debugtools.h"
14 #include "heap.h"
15
16 DEFAULT_DEBUG_CHANNEL(psdrv)
17 #include <ctype.h>
18
19 /* ptr to fonts for which we have afm files */
20 FONTFAMILY *PSDRV_AFMFontList = NULL;
21
22
23 /***********************************************************
24  *
25  *      PSDRV_AFMGetCharMetrics
26  *
27  * Parses CharMetric section of AFM file.
28  *
29  * Actually only collects the widths of numbered chars and puts then in
30  * afm->CharWidths.
31  */
32 static void PSDRV_AFMGetCharMetrics(AFM *afm, FILE *fp)
33 {
34     char line[256], valbuf[256];
35     char *cp, *item, *value, *curpos, *endpos;
36     int i;
37     AFMMETRICS **insert = &afm->Metrics, *metric;
38
39     for(i = 0; i < afm->NumofMetrics; i++) {
40
41         if(!fgets(line, sizeof(line), fp)) {
42            ERR("Unexpected EOF\n");
43            return;
44         }
45
46         metric = *insert = HeapAlloc( PSDRV_Heap, HEAP_ZERO_MEMORY, 
47                                       sizeof(AFMMETRICS) );
48         insert = &metric->next;
49
50         cp = line + strlen(line);
51         do {
52             *cp = '\0';
53             cp--;
54         } while(cp > line && isspace(*cp));
55
56         curpos = line;
57         while(*curpos) {
58             item = curpos;
59             while(isspace(*item))
60                 item++;
61             value = strpbrk(item, " \t");
62             while(isspace(*value))
63                 value++;
64             cp = endpos = strchr(value, ';');
65             while(isspace(*--cp))
66                 ;
67             memcpy(valbuf, value, cp - value + 1);
68             valbuf[cp - value + 1] = '\0';
69             value = valbuf;
70
71             if(!strncmp(item, "C ", 2)) {
72                 value = strchr(item, ' ');
73                 sscanf(value, " %d", &metric->C);
74
75             } else if(!strncmp(item, "CH ", 3)) {
76                 value = strrchr(item, ' ');
77                 sscanf(value, " %x", &metric->C);
78             }
79
80             else if(!strncmp("WX ", item, 3) || !strncmp("W0X ", item, 4)) {
81                 sscanf(value, "%f", &metric->WX);
82                 if(metric->C >= 0 && metric->C <= 0xff)
83                     afm->CharWidths[metric->C] = metric->WX;
84             }
85
86             else if(!strncmp("N ", item, 2)) {
87                 metric->N = HEAP_strdupA( PSDRV_Heap, 0, value);
88             }
89
90             else if(!strncmp("B ", item, 2)) {
91                 sscanf(value, "%f%f%f%f", &metric->B.llx, &metric->B.lly,
92                                           &metric->B.urx, &metric->B.ury);
93
94                 /* Store height of Aring to use as lfHeight */
95                 if(metric->N && !strncmp(metric->N, "Aring", 5))
96                     afm->FullAscender = metric->B.ury;
97             }
98
99             /* Ligatures go here... */
100
101             curpos = endpos + 1;
102         }
103
104 #if 0   
105         TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n",
106               metric->N, metric->WX, metric->B.llx, metric->B.lly,
107               metric->B.urx, metric->B.ury);
108 #endif
109     }
110
111     return;
112 }
113
114 /***********************************************************
115  *
116  *      PSDRV_AFMParse
117  *
118  * Fills out an AFM structure and associated substructures (see psdrv.h)
119  * for a given AFM file. All memory is allocated from the process heap. 
120  * Returns a ptr to the AFM structure or NULL on error.
121  *
122  * This is not complete (we don't handle kerning yet) and not efficient
123  */
124 static AFM *PSDRV_AFMParse(char const *file)
125 {
126     FILE *fp;
127     char buf[256];
128     char *value;
129     AFM *afm;
130     char *cp;
131
132     TRACE("parsing '%s'\n", file);
133
134     if((fp = fopen(file, "r")) == NULL) {
135         MESSAGE("Can't open AFM file '%s'. Please check wine.conf .\n", file);
136         return NULL;
137     }
138
139     afm = HeapAlloc(PSDRV_Heap, HEAP_ZERO_MEMORY, sizeof(AFM));
140     if(!afm) {
141         fclose(fp);
142         return NULL;
143     }
144
145     while(fgets(buf, sizeof(buf), fp)) {
146         cp = buf + strlen(buf);
147         do {
148             *cp = '\0';
149             cp--;
150         } while(cp > buf && isspace(*cp));
151
152         value = strchr(buf, ' ');
153         if(value)
154             while(isspace(*value))
155                 value++;
156
157         if(!strncmp("FontName", buf, 8)) {
158             afm->FontName = HEAP_strdupA(PSDRV_Heap, 0, value);
159             continue;
160         }
161
162         if(!strncmp("FullName", buf, 8)) {
163             afm->FullName = HEAP_strdupA(PSDRV_Heap, 0, value);
164             continue;
165         }
166
167         if(!strncmp("FamilyName", buf, 10)) {
168             afm->FamilyName = HEAP_strdupA(PSDRV_Heap, 0, value);
169             continue;
170         }
171         
172         if(!strncmp("Weight", buf, 6)) {
173             if(!strncmp("Roman", value, 5) || !strncmp("Medium", value, 6)
174                || !strncmp("Book", value, 4) || !strncmp("Regular", value, 7)
175                || !strncmp("Normal", value, 6))
176                 afm->Weight = FW_NORMAL;
177             else if(!strncmp("Demi", value, 4))
178                 afm->Weight = FW_DEMIBOLD;
179             else if(!strncmp("Bold", value, 4))
180                 afm->Weight = FW_BOLD;
181             else if(!strncmp("Light", value, 5))
182                 afm->Weight = FW_LIGHT;
183             else {
184                 FIXME("Unkown AFM Weight '%s'\n", value);
185                 afm->Weight = FW_NORMAL;
186             }
187             continue;
188         }
189
190         if(!strncmp("ItalicAngle", buf, 11)) {
191             sscanf(value, "%f", &(afm->ItalicAngle));
192             continue;
193         }
194
195         if(!strncmp("IsFixedPitch", buf, 12)) {
196             if(!strncasecmp("false", value, 5))
197                 afm->IsFixedPitch = FALSE;
198             else
199                 afm->IsFixedPitch = TRUE;
200             continue;
201         }
202
203         if(!strncmp("FontBBox", buf, 8)) {
204             sscanf(value, "%f %f %f %f", &(afm->FontBBox.llx), 
205                    &(afm->FontBBox.lly), &(afm->FontBBox.urx), 
206                    &(afm->FontBBox.ury) );
207             continue;
208         }
209
210         if(!strncmp("UnderlinePosition", buf, 17)) {
211             sscanf(value, "%f", &(afm->UnderlinePosition) );
212             continue;
213         }
214
215         if(!strncmp("UnderlineThickness", buf, 18)) {
216             sscanf(value, "%f", &(afm->UnderlineThickness) );
217             continue;
218         }
219
220         if(!strncmp("CapHeight", buf, 9)) {
221             sscanf(value, "%f", &(afm->CapHeight) );
222             continue;
223         }
224
225         if(!strncmp("XHeight", buf, 7)) {
226             sscanf(value, "%f", &(afm->XHeight) );
227             continue;
228         }
229
230         if(!strncmp("Ascender", buf, 8)) {
231             sscanf(value, "%f", &(afm->Ascender) );
232             continue;
233         }
234
235         if(!strncmp("Descender", buf, 9)) {
236             sscanf(value, "%f", &(afm->Descender) );
237             continue;
238         }
239
240         if(!strncmp("StartCharMetrics", buf, 16)) {
241             sscanf(value, "%d", &(afm->NumofMetrics) );
242             PSDRV_AFMGetCharMetrics(afm, fp);
243             continue;
244         }
245
246         if(!strncmp("EncodingScheme", buf, 14)) {
247             afm->EncodingScheme = HEAP_strdupA(PSDRV_Heap, 0, value);
248             continue;
249         }
250
251     }
252     fclose(fp);
253
254     if(afm->Ascender == 0.0)
255         afm->Ascender = afm->FontBBox.ury;
256     if(afm->Descender == 0.0)
257         afm->Descender = afm->FontBBox.lly;
258     if(afm->FullAscender == 0.0)
259         afm->FullAscender = afm->Ascender;
260
261     return afm;
262 }
263
264 /***********************************************************
265  *
266  *      PSDRV_FreeAFMList
267  *
268  * Frees the family and afmlistentry structures in list head
269  */
270 void PSDRV_FreeAFMList( FONTFAMILY *head )
271 {
272     AFMLISTENTRY *afmle, *nexta;
273     FONTFAMILY *family, *nextf;
274
275     for(nextf = family = head; nextf; family = nextf) {
276         for(nexta = afmle = family->afmlist; nexta; afmle = nexta) {
277             nexta = afmle->next;
278             HeapFree( PSDRV_Heap, 0, afmle );
279         }
280         nextf = family->next;
281         HeapFree( PSDRV_Heap, 0, family );
282     }
283     return;
284 }
285
286
287 /***********************************************************
288  *
289  *      PSDRV_FindAFMinList
290  * Returns ptr to an AFM if name (which is a PS font name) exists in list
291  * headed by head.
292  */
293 AFM *PSDRV_FindAFMinList(FONTFAMILY *head, char *name)
294 {
295     FONTFAMILY *family;
296     AFMLISTENTRY *afmle;
297
298     for(family = head; family; family = family->next) {
299         for(afmle = family->afmlist; afmle; afmle = afmle->next) {
300             if(!strcmp(afmle->afm->FontName, name))
301                 return afmle->afm;
302         }
303     }
304     return NULL;
305 }
306
307 /***********************************************************
308  *
309  *      PSDRV_AddAFMtoList
310  *
311  * Adds an afm to the list whose head is pointed to by head. Creates new
312  * family node if necessary and always creates a new AFMLISTENTRY.
313  */
314 void PSDRV_AddAFMtoList(FONTFAMILY **head, AFM *afm)
315 {
316     FONTFAMILY *family = *head;
317     FONTFAMILY **insert = head;
318     AFMLISTENTRY *tmpafmle, *newafmle;
319
320     newafmle = HeapAlloc(PSDRV_Heap, HEAP_ZERO_MEMORY,
321                            sizeof(*newafmle));
322     newafmle->afm = afm;
323
324     while(family) {
325         if(!strcmp(family->FamilyName, afm->FamilyName))
326             break;
327         insert = &(family->next);
328         family = family->next;
329     }
330  
331     if(!family) {
332         family = HeapAlloc(PSDRV_Heap, HEAP_ZERO_MEMORY,
333                            sizeof(*family));
334         *insert = family;
335         family->FamilyName = HEAP_strdupA(PSDRV_Heap, 0,
336                                           afm->FamilyName);
337         family->afmlist = newafmle;
338         return;
339     }
340     
341     tmpafmle = family->afmlist;
342     while(tmpafmle->next)
343         tmpafmle = tmpafmle->next;
344
345     tmpafmle->next = newafmle;
346
347     return;
348 }
349
350 /**********************************************************
351  *
352  *      PSDRV_ReencodeCharWidths
353  *
354  * Re map the CharWidths field of the afm to correspond to an ANSI encoding
355  *
356  */
357 static void PSDRV_ReencodeCharWidths(AFM *afm)
358 {
359     int i;
360     AFMMETRICS *metric;
361
362     for(i = 0; i < 256; i++) {
363         if(isalnum(i))
364             continue;
365         if(PSDRV_ANSIVector[i] == NULL) {
366             afm->CharWidths[i] = 0.0;
367             continue;
368         }
369         for(metric = afm->Metrics; metric; metric = metric->next) {
370             if(!strcmp(metric->N, PSDRV_ANSIVector[i])) {
371                 afm->CharWidths[i] = metric->WX;
372                 break;
373             }
374         }
375         if(!metric) {
376             WARN("Couldn't find glyph '%s' in font '%s'\n",
377                  PSDRV_ANSIVector[i], afm->FontName);
378             afm->CharWidths[i] = 0.0;
379         }
380     }
381     return;
382 }
383
384 /***********************************************************
385  *
386  *      PSDRV_afmfilesCallback
387  *
388  * Callback for PROFILE_EnumerateWineIniSection
389  * Try to parse AFM file `value', alter the CharWidths field of afm struct if
390  * the font is using AdobeStandardEncoding to correspond to WinANSI, then add
391  * afm to system font list.
392  */
393 static void PSDRV_afmfilesCallback(char const *key, char const *value,
394 void *user)
395 {
396     AFM *afm;
397
398     afm = PSDRV_AFMParse(value);
399     if(afm) {
400         if(afm->EncodingScheme && 
401            !strcmp(afm->EncodingScheme, "AdobeStandardEncoding")) {
402             PSDRV_ReencodeCharWidths(afm);
403         }
404         PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm);
405     }
406     return;
407 }
408
409
410 /***********************************************************
411  *
412  *      PSDRV_DumpFontList
413  *
414  */
415 static void PSDRV_DumpFontList(void)
416 {
417     FONTFAMILY *family;
418     AFMLISTENTRY *afmle;
419
420     for(family = PSDRV_AFMFontList; family; family = family->next) {
421         TRACE("Family '%s'\n", family->FamilyName);
422         for(afmle = family->afmlist; afmle; afmle = afmle->next) {
423             TRACE("\tFontName '%s'\n", afmle->afm->FontName);
424         }
425     }
426     return;
427 }
428
429
430 /***********************************************************
431  *
432  *      PSDRV_GetFontMetrics
433  *
434  * Only exported function in this file. Parses all afm files listed in
435  * [afmfiles] of wine.conf .
436  */
437 BOOL PSDRV_GetFontMetrics(void)
438 {
439     PROFILE_EnumerateWineIniSection( "afmfiles", PSDRV_afmfilesCallback, NULL);
440     PSDRV_DumpFontList();
441     return TRUE;
442 }
443