]> git.wh0rd.org - fontconfig.git/blob - src/fclist.c
Adopt some RedHat suggestions for standard font configuration.
[fontconfig.git] / src / fclist.c
1 /*
2 * $RCSId: xc/lib/fontconfig/src/fclist.c,v 1.11tsi Exp $
3 *
4 * Copyright © 2000 Keith Packard
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 int high, low, mid, c;
49
50 if (os->nobject == os->sobject)
51 {
52 s = os->sobject + 4;
53 if (os->objects)
54 objects = (const char **) realloc ((void *) os->objects,
55 s * sizeof (const char *));
56 else
57 objects = (const char **) malloc (s * sizeof (const char *));
58 if (!objects)
59 return FcFalse;
60 if (os->sobject)
61 FcMemFree (FC_MEM_OBJECTPTR, os->sobject * sizeof (const char *));
62 FcMemAlloc (FC_MEM_OBJECTPTR, s * sizeof (const char *));
63 os->objects = objects;
64 os->sobject = s;
65 }
66 high = os->nobject - 1;
67 low = 0;
68 mid = 0;
69 c = 1;
70 while (low <= high)
71 {
72 mid = (low + high) >> 1;
73 c = strcmp (os->objects[mid], object);
74 if (c == 0)
75 return FcTrue;
76 if (c < 0)
77 low = mid + 1;
78 else
79 high = mid - 1;
80 }
81 if (c < 0)
82 mid++;
83 memmove (os->objects + mid + 1, os->objects + mid,
84 (os->nobject - mid) * sizeof (const char *));
85 os->objects[mid] = FcObjectStaticName (object);
86 os->nobject++;
87 return FcTrue;
88 }
89
90 void
91 FcObjectSetDestroy (FcObjectSet *os)
92 {
93 if (os->objects)
94 {
95 FcMemFree (FC_MEM_OBJECTPTR, os->sobject * sizeof (const char *));
96 free ((void *) os->objects);
97 }
98 FcMemFree (FC_MEM_OBJECTSET, sizeof (FcObjectSet));
99 free (os);
100 }
101
102 FcObjectSet *
103 FcObjectSetVaBuild (const char *first, va_list va)
104 {
105 FcObjectSet *ret;
106
107 FcObjectSetVapBuild (ret, first, va);
108 return ret;
109 }
110
111 FcObjectSet *
112 FcObjectSetBuild (const char *first, ...)
113 {
114 va_list va;
115 FcObjectSet *os;
116
117 va_start (va, first);
118 FcObjectSetVapBuild (os, first, va);
119 va_end (va);
120 return os;
121 }
122
123 /*
124 * Font must have a containing value for every value in the pattern
125 */
126 static FcBool
127 FcListValueListMatchAny (FcValueList *patOrig, /* pattern */
128 FcValueList *fntOrig) /* font */
129 {
130 FcValueList *pat, *fnt;
131
132 for (pat = patOrig; pat; pat = pat->next)
133 {
134 for (fnt = fntOrig; fnt; fnt = fnt->next)
135 {
136 /*
137 * make sure the font 'contains' the pattern.
138 * (OpListing is OpContains except for strings
139 * where it requires an exact match)
140 */
141 if (FcConfigCompareValue (fnt->value,
142 FcOpListing,
143 pat->value))
144 break;
145 }
146 if (!fnt)
147 return FcFalse;
148 }
149 return FcTrue;
150 }
151
152 static FcBool
153 FcListValueListEqual (FcValueList *v1orig,
154 FcValueList *v2orig)
155 {
156 FcValueList *v1, *v2;
157
158 for (v1 = v1orig; v1; v1 = v1->next)
159 {
160 for (v2 = v2orig; v2; v2 = v2->next)
161 if (FcValueEqual (v1->value, v2->value))
162 break;
163 if (!v2)
164 return FcFalse;
165 }
166 for (v2 = v2orig; v2; v2 = v2->next)
167 {
168 for (v1 = v1orig; v1; v1 = v1->next)
169 if (FcValueEqual (v1->value, v2->value))
170 break;
171 if (!v1)
172 return FcFalse;
173 }
174 return FcTrue;
175 }
176
177 static FcBool
178 FcListPatternEqual (FcPattern *p1,
179 FcPattern *p2,
180 FcObjectSet *os)
181 {
182 int i;
183 FcPatternElt *e1, *e2;
184
185 for (i = 0; i < os->nobject; i++)
186 {
187 e1 = FcPatternFindElt (p1, os->objects[i]);
188 e2 = FcPatternFindElt (p2, os->objects[i]);
189 if (!e1 && !e2)
190 continue;
191 if (!e1 || !e2)
192 return FcFalse;
193 if (!FcListValueListEqual (e1->values, e2->values))
194 return FcFalse;
195 }
196 return FcTrue;
197 }
198
199 /*
200 * FcTrue iff all objects in "p" match "font"
201 */
202
203 FcBool
204 FcListPatternMatchAny (const FcPattern *p,
205 const FcPattern *font)
206 {
207 int i;
208 FcPatternElt *e;
209
210 for (i = 0; i < p->num; i++)
211 {
212 e = FcPatternFindElt (font, p->elts[i].object);
213 if (!e)
214 return FcFalse;
215 if (!FcListValueListMatchAny (p->elts[i].values, /* pat elts */
216 e->values)) /* font elts */
217 return FcFalse;
218 }
219 return FcTrue;
220 }
221
222 static FcChar32
223 FcListMatrixHash (const FcMatrix *m)
224 {
225 int xx = (int) (m->xx * 100),
226 xy = (int) (m->xy * 100),
227 yx = (int) (m->yx * 100),
228 yy = (int) (m->yy * 100);
229
230 return ((FcChar32) xx) ^ ((FcChar32) xy) ^ ((FcChar32) yx) ^ ((FcChar32) yy);
231 }
232
233 static FcChar32
234 FcListValueHash (FcValue v)
235 {
236 switch (v.type) {
237 case FcTypeVoid:
238 return 0;
239 case FcTypeInteger:
240 return (FcChar32) v.u.i;
241 case FcTypeDouble:
242 return (FcChar32) (int) v.u.d;
243 case FcTypeString:
244 return FcStrHashIgnoreCase (v.u.s);
245 case FcTypeBool:
246 return (FcChar32) v.u.b;
247 case FcTypeMatrix:
248 return FcListMatrixHash (v.u.m);
249 case FcTypeCharSet:
250 return FcCharSetCount (v.u.c);
251 case FcTypeFTFace:
252 return (long) v.u.f;
253 case FcTypeLangSet:
254 return FcLangSetHash (v.u.l);
255 }
256 return 0;
257 }
258
259 static FcChar32
260 FcListValueListHash (FcValueList *list)
261 {
262 FcChar32 h = 0;
263
264 while (list)
265 {
266 h = h ^ FcListValueHash (list->value);
267 list = list->next;
268 }
269 return h;
270 }
271
272 static FcChar32
273 FcListPatternHash (FcPattern *font,
274 FcObjectSet *os)
275 {
276 int n;
277 FcPatternElt *e;
278 FcChar32 h = 0;
279
280 for (n = 0; n < os->nobject; n++)
281 {
282 e = FcPatternFindElt (font, os->objects[n]);
283 if (e)
284 h = h ^ FcListValueListHash (e->values);
285 }
286 return h;
287 }
288
289 typedef struct _FcListBucket {
290 struct _FcListBucket *next;
291 FcChar32 hash;
292 FcPattern *pattern;
293 } FcListBucket;
294
295 #define FC_LIST_HASH_SIZE 4099
296
297 typedef struct _FcListHashTable {
298 int entries;
299 FcListBucket *buckets[FC_LIST_HASH_SIZE];
300 } FcListHashTable;
301
302 static void
303 FcListHashTableInit (FcListHashTable *table)
304 {
305 table->entries = 0;
306 memset (table->buckets, '\0', sizeof (table->buckets));
307 }
308
309 static void
310 FcListHashTableCleanup (FcListHashTable *table)
311 {
312 int i;
313 FcListBucket *bucket, *next;
314
315 for (i = 0; i < FC_LIST_HASH_SIZE; i++)
316 {
317 for (bucket = table->buckets[i]; bucket; bucket = next)
318 {
319 next = bucket->next;
320 FcPatternDestroy (bucket->pattern);
321 FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
322 free (bucket);
323 }
324 table->buckets[i] = 0;
325 }
326 table->entries = 0;
327 }
328
329 static FcBool
330 FcListAppend (FcListHashTable *table,
331 FcPattern *font,
332 FcObjectSet *os)
333 {
334 int o;
335 FcPatternElt *e;
336 FcValueList *v;
337 FcChar32 hash;
338 FcListBucket **prev, *bucket;
339
340 hash = FcListPatternHash (font, os);
341 for (prev = &table->buckets[hash % FC_LIST_HASH_SIZE];
342 (bucket = *prev); prev = &(bucket->next))
343 {
344 if (bucket->hash == hash &&
345 FcListPatternEqual (bucket->pattern, font, os))
346 return FcTrue;
347 }
348 bucket = (FcListBucket *) malloc (sizeof (FcListBucket));
349 if (!bucket)
350 goto bail0;
351 FcMemAlloc (FC_MEM_LISTBUCK, sizeof (FcListBucket));
352 bucket->next = 0;
353 bucket->hash = hash;
354 bucket->pattern = FcPatternCreate ();
355 if (!bucket->pattern)
356 goto bail1;
357
358 for (o = 0; o < os->nobject; o++)
359 {
360 e = FcPatternFindElt (font, os->objects[o]);
361 if (e)
362 {
363 for (v = e->values; v; v = v->next)
364 {
365 if (!FcPatternAdd (bucket->pattern,
366 os->objects[o],
367 v->value, FcTrue))
368 goto bail2;
369 }
370 }
371 }
372 *prev = bucket;
373 ++table->entries;
374
375 return FcTrue;
376
377 bail2:
378 FcPatternDestroy (bucket->pattern);
379 bail1:
380 FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
381 free (bucket);
382 bail0:
383 return FcFalse;
384 }
385
386 FcFontSet *
387 FcFontSetList (FcConfig *config,
388 FcFontSet **sets,
389 int nsets,
390 FcPattern *p,
391 FcObjectSet *os)
392 {
393 FcFontSet *ret;
394 FcFontSet *s;
395 int f;
396 int set;
397 FcListHashTable table;
398 int i;
399 FcListBucket *bucket;
400
401 if (!config)
402 {
403 if (!FcInitBringUptoDate ())
404 goto bail0;
405
406 config = FcConfigGetCurrent ();
407 if (!config)
408 goto bail0;
409 }
410 FcListHashTableInit (&table);
411 /*
412 * Walk all available fonts adding those that
413 * match to the hash table
414 */
415 for (set = 0; set < nsets; set++)
416 {
417 s = sets[set];
418 if (!s)
419 continue;
420 for (f = 0; f < s->nfont; f++)
421 if (FcListPatternMatchAny (p, /* pattern */
422 s->fonts[f])) /* font */
423 if (!FcListAppend (&table, s->fonts[f], os))
424 goto bail1;
425 }
426 #if 0
427 {
428 int max = 0;
429 int full = 0;
430 int ents = 0;
431 int len;
432 for (i = 0; i < FC_LIST_HASH_SIZE; i++)
433 {
434 if ((bucket = table.buckets[i]))
435 {
436 len = 0;
437 for (; bucket; bucket = bucket->next)
438 {
439 ents++;
440 len++;
441 }
442 if (len > max)
443 max = len;
444 full++;
445 }
446 }
447 printf ("used: %d max: %d avg: %g\n", full, max,
448 (double) ents / FC_LIST_HASH_SIZE);
449 }
450 #endif
451 /*
452 * Walk the hash table and build
453 * a font set
454 */
455 ret = FcFontSetCreate ();
456 if (!ret)
457 goto bail0;
458 for (i = 0; i < FC_LIST_HASH_SIZE; i++)
459 while ((bucket = table.buckets[i]))
460 {
461 if (!FcFontSetAdd (ret, bucket->pattern))
462 goto bail2;
463 table.buckets[i] = bucket->next;
464 FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
465 free (bucket);
466 }
467
468 return ret;
469
470 bail2:
471 FcFontSetDestroy (ret);
472 bail1:
473 FcListHashTableCleanup (&table);
474 bail0:
475 return 0;
476 }
477
478 FcFontSet *
479 FcFontList (FcConfig *config,
480 FcPattern *p,
481 FcObjectSet *os)
482 {
483 FcFontSet *sets[2];
484 int nsets;
485
486 if (!config)
487 {
488 config = FcConfigGetCurrent ();
489 if (!config)
490 return 0;
491 }
492 nsets = 0;
493 if (config->fonts[FcSetSystem])
494 sets[nsets++] = config->fonts[FcSetSystem];
495 if (config->fonts[FcSetApplication])
496 sets[nsets++] = config->fonts[FcSetApplication];
497 return FcFontSetList (config, sets, nsets, p, os);
498 }