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