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