]> git.wh0rd.org Git - fontconfig.git/blob - src/fclist.c
Add aspect ratio support to Xft and fontconfig
[fontconfig.git] / src / fclist.c
1 /*
2  * $XFree86: xc/lib/fontconfig/src/fclist.c,v 1.2 2002/02/28 16:51:48 keithp Exp $
3  *
4  * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24
25 #include <stdlib.h>
26 #include "fcint.h"
27
28 FcObjectSet *
29 FcObjectSetCreate (void)
30 {
31     FcObjectSet    *os;
32
33     os = (FcObjectSet *) malloc (sizeof (FcObjectSet));
34     if (!os)
35         return 0;
36     FcMemAlloc (FC_MEM_OBJECTSET, sizeof (FcObjectSet));
37     os->nobject = 0;
38     os->sobject = 0;
39     os->objects = 0;
40     return os;
41 }
42
43 FcBool
44 FcObjectSetAdd (FcObjectSet *os, const char *object)
45 {
46     int         s;
47     const char  **objects;
48     
49     if (os->nobject == os->sobject)
50     {
51         s = os->sobject + 4;
52         if (os->objects)
53             objects = (const char **) realloc ((void *) os->objects,
54                                                s * sizeof (const char *));
55         else
56             objects = (const char **) malloc (s * sizeof (const char *));
57         if (!objects)
58             return FcFalse;
59         if (os->sobject)
60             FcMemFree (FC_MEM_OBJECTPTR, os->sobject * sizeof (const char *));
61         FcMemAlloc (FC_MEM_OBJECTPTR, s * sizeof (const char *));
62         os->objects = objects;
63         os->sobject = s;
64     }
65     os->objects[os->nobject++] = object;
66     return FcTrue;
67 }
68
69 void
70 FcObjectSetDestroy (FcObjectSet *os)
71 {
72     if (os->objects)
73     {
74         FcMemFree (FC_MEM_OBJECTPTR, os->sobject * sizeof (const char *));
75         free ((void *) os->objects);
76     }
77     FcMemFree (FC_MEM_OBJECTSET, sizeof (FcObjectSet));
78     free (os);
79 }
80
81 FcObjectSet *
82 FcObjectSetVaBuild (const char *first, va_list va)
83 {
84     FcObjectSet    *ret;
85
86     FcObjectSetVapBuild (ret, first, va);
87     return ret;
88 }
89
90 FcObjectSet *
91 FcObjectSetBuild (const char *first, ...)
92 {
93     va_list         va;
94     FcObjectSet    *os;
95
96     va_start (va, first);
97     FcObjectSetVapBuild (os, first, va);
98     va_end (va);
99     return os;
100 }
101
102 static FcBool
103 FcListValueListMatchAny (FcValueList *v1orig,
104                          FcValueList *v2orig)
105 {
106     FcValueList     *v1, *v2;
107
108     for (v1 = v1orig; v1; v1 = v1->next)
109         for (v2 = v2orig; v2; v2 = v2->next)
110             if (FcConfigCompareValue (v2->value, FcOpContains, v1->value))
111                 return FcTrue;
112     return FcFalse;
113 }
114
115 static FcBool
116 FcListValueListEqual (FcValueList   *v1orig,
117                       FcValueList   *v2orig)
118 {
119     FcValueList     *v1, *v2;
120
121     for (v1 = v1orig; v1; v1 = v1->next)
122     {
123         for (v2 = v2orig; v2; v2 = v2->next)
124             if (FcConfigCompareValue (v1->value, FcOpEqual, v2->value))
125                 break;
126         if (!v2)
127             return FcFalse;
128     }
129     for (v2 = v2orig; v2; v2 = v2->next)
130     {
131         for (v1 = v1orig; v1; v1 = v1->next)
132             if (FcConfigCompareValue (v1->value, FcOpEqual, v2->value))
133                 break;
134         if (!v1)
135             return FcFalse;
136     }
137     return FcTrue;
138 }
139
140 /*
141  * FcTrue iff all objects in "p" match "font"
142  */
143
144 static FcBool
145 FcListPatternMatchAny (FcPattern *p,
146                        FcPattern *font)
147 {
148     int             i;
149     FcPatternElt   *e;
150
151     for (i = 0; i < p->num; i++)
152     {
153         e = FcPatternFind (font, p->elts[i].object, FcFalse);
154         if (!e)
155             return FcFalse;
156         if (!FcListValueListMatchAny (p->elts[i].values, e->values))
157             return FcFalse;
158     }
159     return FcTrue;
160 }
161
162 static FcBool
163 FcListPatternEqual (FcPattern   *p1,
164                     FcPattern   *p2,
165                     FcObjectSet *os)
166 {
167     int             i;
168     FcPatternElt    *e1, *e2;
169
170     for (i = 0; i < os->nobject; i++)
171     {
172         e1 = FcPatternFind (p1, os->objects[i], FcFalse);
173         e2 = FcPatternFind (p2, os->objects[i], FcFalse);
174         if (!e1 && !e2)
175             return FcTrue;
176         if (!e1 || !e2)
177             return FcFalse;
178         if (!FcListValueListEqual (e1->values, e2->values))
179             return FcFalse;
180     }
181     return FcTrue;
182 }
183
184 static FcChar32
185 FcListStringHash (const FcChar8 *s)
186 {
187     FcChar32    h = 0;
188     FcChar8     c;
189
190     while ((c = *s++))
191     {
192         c = FcToLower (c);
193         h = ((h << 3) ^ (h >> 3)) ^ c;
194     }
195     return h;
196 }
197
198 static FcChar32
199 FcListMatrixHash (const FcMatrix *m)
200 {
201     int     xx = (int) (m->xx * 100), 
202             xy = (int) (m->xy * 100), 
203             yx = (int) (m->yx * 100),
204             yy = (int) (m->yy * 100);
205
206     return ((FcChar32) xx) ^ ((FcChar32) xy) ^ ((FcChar32) yx) ^ ((FcChar32) yy);
207 }
208
209 static FcChar32
210 FcListValueHash (FcValue    v)
211 {
212     switch (v.type) {
213     case FcTypeVoid:
214         return 0;
215     case FcTypeInteger:
216         return (FcChar32) v.u.i;
217     case FcTypeDouble:
218         return (FcChar32) (int) v.u.d;
219     case FcTypeString:
220         return FcListStringHash (v.u.s);
221     case FcTypeBool:
222         return (FcChar32) v.u.b;
223     case FcTypeMatrix:
224         return FcListMatrixHash (v.u.m);
225     case FcTypeCharSet:
226         return FcCharSetCount (v.u.c);
227     }
228     return 0;
229 }
230
231 static FcChar32
232 FcListValueListHash (FcValueList    *list)
233 {
234     FcChar32    h = 0;
235     
236     while (list)
237     {
238         h = h ^ FcListValueHash (list->value);
239         list = list->next;
240     }
241     return h;
242 }
243
244 static FcChar32
245 FcListPatternHash (FcPattern    *font,
246                    FcObjectSet  *os)
247 {
248     int             n;
249     FcPatternElt    *e;
250     FcChar32        h = 0;
251
252     for (n = 0; n < os->nobject; n++)
253     {
254         e = FcPatternFind (font, os->objects[n], FcFalse);
255         if (e)
256             h = h ^ FcListValueListHash (e->values);
257     }
258     return h;
259 }
260
261 typedef struct _FcListBucket {
262     struct _FcListBucket    *next;
263     FcChar32                hash;
264     FcPattern               *pattern;
265 } FcListBucket;
266
267 #define FC_LIST_HASH_SIZE   4099
268
269 typedef struct _FcListHashTable {
270     int             entries;
271     FcListBucket    *buckets[FC_LIST_HASH_SIZE];
272 } FcListHashTable;
273     
274 static void
275 FcListHashTableInit (FcListHashTable *table)
276 {
277     table->entries = 0;
278     memset (table->buckets, '\0', sizeof (table->buckets));
279 }
280
281 static void
282 FcListHashTableCleanup (FcListHashTable *table)
283 {
284     int i;
285     FcListBucket    *bucket, *next;
286
287     for (i = 0; i < FC_LIST_HASH_SIZE; i++)
288     {
289         for (bucket = table->buckets[i]; bucket; bucket = next)
290         {
291             next = bucket->next;
292             FcPatternDestroy (bucket->pattern);
293             FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
294             free (bucket);
295         }
296         table->buckets[i] = 0;
297     }
298     table->entries = 0;
299 }
300
301 static FcBool
302 FcListAppend (FcListHashTable   *table,
303               FcPattern         *font,
304               FcObjectSet       *os)
305 {
306     int             o;
307     FcPatternElt    *e;
308     FcValueList     *v;
309     FcChar32        hash;
310     FcListBucket    **prev, *bucket;
311
312     hash = FcListPatternHash (font, os);
313     for (prev = &table->buckets[hash % FC_LIST_HASH_SIZE];
314          (bucket = *prev); prev = &(bucket->next))
315     {
316         if (bucket->hash == hash && 
317             FcListPatternEqual (bucket->pattern, font, os))
318             return FcTrue;
319     }
320     bucket = (FcListBucket *) malloc (sizeof (FcListBucket));
321     if (!bucket)
322         goto bail0;
323     FcMemAlloc (FC_MEM_LISTBUCK, sizeof (FcListBucket));
324     bucket->next = 0;
325     bucket->hash = hash;
326     bucket->pattern = FcPatternCreate ();
327     if (!bucket->pattern)
328         goto bail1;
329     
330     for (o = 0; o < os->nobject; o++)
331     {
332         e = FcPatternFind (font, os->objects[o], FcFalse);
333         if (e)
334         {
335             for (v = e->values; v; v = v->next)
336             {
337                 if (!FcPatternAdd (bucket->pattern, 
338                                    os->objects[o], 
339                                    v->value, FcTrue))
340                     goto bail2;
341             }
342         }
343     }
344     *prev = bucket;
345     ++table->entries;
346
347     return FcTrue;
348     
349 bail2:
350     FcPatternDestroy (bucket->pattern);
351 bail1:
352     FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
353     free (bucket);
354 bail0:
355     return FcFalse;
356 }
357
358 FcFontSet *
359 FcFontSetList (FcConfig     *config,
360                FcFontSet    **sets,
361                int          nsets,
362                FcPattern    *p,
363                FcObjectSet  *os)
364 {
365     FcFontSet       *ret;
366     FcFontSet       *s;
367     int             f;
368     int             set;
369     FcListHashTable table;
370     int             i;
371     FcListBucket    *bucket;
372
373     if (!config)
374     {
375         if (!FcInitBringUptoDate ())
376             goto bail0;
377
378         config = FcConfigGetCurrent ();
379         if (!config)
380             goto bail0;
381     }
382     FcListHashTableInit (&table);
383     /*
384      * Walk all available fonts adding those that
385      * match to the hash table
386      */
387     for (set = 0; set < nsets; set++)
388     {
389         s = sets[set];
390         if (!s)
391             continue;
392         for (f = 0; f < s->nfont; f++)
393             if (FcListPatternMatchAny (p, s->fonts[f]))
394                 if (!FcListAppend (&table, s->fonts[f], os))
395                     goto bail1;
396     }
397 #if 0
398     {
399         int     max = 0;
400         int     full = 0;
401         int     ents = 0;
402         int     len;
403         for (i = 0; i < FC_LIST_HASH_SIZE; i++)
404         {
405             if ((bucket = table.buckets[i]))
406             {
407                 len = 0;
408                 for (; bucket; bucket = bucket->next)
409                 {
410                     ents++;
411                     len++;
412                 }
413                 if (len > max)
414                     max = len;
415                 full++;
416             }
417         }
418         printf ("used: %d max: %d avg: %g\n", full, max, 
419                 (double) ents / FC_LIST_HASH_SIZE);
420     }
421 #endif
422     /*
423      * Walk the hash table and build
424      * a font set
425      */
426     ret = FcFontSetCreate ();
427     if (!ret)
428         goto bail0;
429     for (i = 0; i < FC_LIST_HASH_SIZE; i++)
430         while ((bucket = table.buckets[i]))
431         {
432             if (!FcFontSetAdd (ret, bucket->pattern))
433                 goto bail2;
434             table.buckets[i] = bucket->next;
435             FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
436             free (bucket);
437         }
438     
439     return ret;
440
441 bail2:
442     FcFontSetDestroy (ret);
443 bail1:
444     FcListHashTableCleanup (&table);
445 bail0:
446     return 0;
447 }
448
449 FcFontSet *
450 FcFontList (FcConfig    *config,
451             FcPattern   *p,
452             FcObjectSet *os)
453 {
454     FcFontSet   *sets[2];
455     int         nsets;
456
457     if (!config)
458     {
459         config = FcConfigGetCurrent ();
460         if (!config)
461             return 0;
462     }
463     nsets = 0;
464     if (config->fonts[FcSetSystem])
465         sets[nsets++] = config->fonts[FcSetSystem];
466     if (config->fonts[FcSetApplication])
467         sets[nsets++] = config->fonts[FcSetApplication];
468     return FcFontSetList (config, sets, nsets, p, os);
469 }