fclist.c revision a6844aab
1/*
2 * fontconfig/src/fclist.c
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 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE AUTHOR(S) 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 "fcint.h"
26#include <stdlib.h>
27
28FcObjectSet *
29FcObjectSetCreate (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
43FcBool
44FcObjectSetAdd (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    object = (char *)FcStrStaticName ((FcChar8 *)object);
71    while (low <= high)
72    {
73	mid = (low + high) >> 1;
74	c = os->objects[mid] - object;
75	if (c == 0)
76	    return FcTrue;
77	if (c < 0)
78	    low = mid + 1;
79	else
80	    high = mid - 1;
81    }
82    if (c < 0)
83	mid++;
84    memmove (os->objects + mid + 1, os->objects + mid,
85	     (os->nobject - mid) * sizeof (const char *));
86    os->objects[mid] = object;
87    os->nobject++;
88    return FcTrue;
89}
90
91void
92FcObjectSetDestroy (FcObjectSet *os)
93{
94    if (os->objects)
95    {
96	FcMemFree (FC_MEM_OBJECTPTR, os->sobject * sizeof (const char *));
97	free ((void *) os->objects);
98    }
99    FcMemFree (FC_MEM_OBJECTSET, sizeof (FcObjectSet));
100    free (os);
101}
102
103FcObjectSet *
104FcObjectSetVaBuild (const char *first, va_list va)
105{
106    FcObjectSet    *ret;
107
108    FcObjectSetVapBuild (ret, first, va);
109    return ret;
110}
111
112FcObjectSet *
113FcObjectSetBuild (const char *first, ...)
114{
115    va_list	    va;
116    FcObjectSet    *os;
117
118    va_start (va, first);
119    FcObjectSetVapBuild (os, first, va);
120    va_end (va);
121    return os;
122}
123
124/*
125 * Font must have a containing value for every value in the pattern
126 */
127static FcBool
128FcListValueListMatchAny (FcValueListPtr patOrig,	    /* pattern */
129			 FcValueListPtr fntOrig)	    /* font */
130{
131    FcValueListPtr	 pat, fnt;
132
133    for (pat = patOrig; pat != NULL; pat = FcValueListNext(pat))
134    {
135	for (fnt = fntOrig; fnt != NULL; fnt = FcValueListNext(fnt))
136	{
137	    /*
138	     * make sure the font 'contains' the pattern.
139	     * (OpListing is OpContains except for strings
140	     *  where it requires an exact match)
141	     */
142	    if (FcConfigCompareValue (&fnt->value,
143				      FcOpListing,
144				      &pat->value))
145		break;
146	}
147	if (fnt == NULL)
148	    return FcFalse;
149    }
150    return FcTrue;
151}
152
153static FcBool
154FcListValueListEqual (FcValueListPtr v1orig,
155		      FcValueListPtr v2orig)
156{
157    FcValueListPtr	    v1, v2;
158
159    for (v1 = v1orig; v1 != NULL; v1 = FcValueListNext(v1))
160    {
161	for (v2 = v2orig; v2 != NULL; v2 = FcValueListNext(v2))
162	    if (FcValueEqual (FcValueCanonicalize(&(v1)->value),
163			      FcValueCanonicalize(&(v2)->value)))
164		break;
165	if (v2 == NULL)
166	    return FcFalse;
167    }
168    for (v2 = v2orig; v2 != NULL; v2 = FcValueListNext(v2))
169    {
170	for (v1 = v1orig; v1 != NULL; v1 = FcValueListNext(v1))
171	    if (FcValueEqual (FcValueCanonicalize(&v1->value),
172			      FcValueCanonicalize(&v2->value)))
173		break;
174	if (v1 == NULL)
175	    return FcFalse;
176    }
177    return FcTrue;
178}
179
180static FcBool
181FcListPatternEqual (FcPattern	*p1,
182		    FcPattern	*p2,
183		    FcObjectSet	*os)
184{
185    int		    i;
186    FcPatternElt    *e1, *e2;
187
188    for (i = 0; i < os->nobject; i++)
189    {
190	e1 = FcPatternObjectFindElt (p1, FcObjectFromName (os->objects[i]));
191	e2 = FcPatternObjectFindElt (p2, FcObjectFromName (os->objects[i]));
192	if (!e1 && !e2)
193	    continue;
194	if (!e1 || !e2)
195	    return FcFalse;
196	if (!FcListValueListEqual (FcPatternEltValues(e1),
197				   FcPatternEltValues(e2)))
198	    return FcFalse;
199    }
200    return FcTrue;
201}
202
203/*
204 * FcTrue iff all objects in "p" match "font"
205 */
206
207FcBool
208FcListPatternMatchAny (const FcPattern *p,
209		       const FcPattern *font)
210{
211    int		    i;
212
213    for (i = 0; i < p->num; i++)
214    {
215	FcPatternElt	*pe = &FcPatternElts(p)[i];
216	FcPatternElt	*fe = FcPatternObjectFindElt (font, pe->object);
217	if (!fe)
218	    return FcFalse;
219	if (!FcListValueListMatchAny (FcPatternEltValues(pe),    /* pat elts */
220				      FcPatternEltValues(fe)))   /* font elts */
221	    return FcFalse;
222    }
223    return FcTrue;
224}
225
226static FcChar32
227FcListMatrixHash (const FcMatrix *m)
228{
229    int	    xx = (int) (m->xx * 100),
230	    xy = (int) (m->xy * 100),
231	    yx = (int) (m->yx * 100),
232	    yy = (int) (m->yy * 100);
233
234    return ((FcChar32) xx) ^ ((FcChar32) xy) ^ ((FcChar32) yx) ^ ((FcChar32) yy);
235}
236
237static FcChar32
238FcListValueHash (FcValue    *value)
239{
240    FcValue v = FcValueCanonicalize(value);
241    switch (v.type) {
242    case FcTypeVoid:
243	return 0;
244    case FcTypeInteger:
245	return (FcChar32) v.u.i;
246    case FcTypeDouble:
247	return (FcChar32) (int) v.u.d;
248    case FcTypeString:
249	return FcStrHashIgnoreCase (v.u.s);
250    case FcTypeBool:
251	return (FcChar32) v.u.b;
252    case FcTypeMatrix:
253	return FcListMatrixHash (v.u.m);
254    case FcTypeCharSet:
255	return FcCharSetCount (v.u.c);
256    case FcTypeFTFace:
257	return (long) v.u.f;
258    case FcTypeLangSet:
259	return FcLangSetHash (v.u.l);
260    }
261    return 0;
262}
263
264static FcChar32
265FcListValueListHash (FcValueListPtr list)
266{
267    FcChar32	h = 0;
268
269    while (list != NULL)
270    {
271	h = h ^ FcListValueHash (&list->value);
272	list = FcValueListNext(list);
273    }
274    return h;
275}
276
277static FcChar32
278FcListPatternHash (FcPattern	*font,
279		   FcObjectSet	*os)
280{
281    int		    n;
282    FcPatternElt    *e;
283    FcChar32	    h = 0;
284
285    for (n = 0; n < os->nobject; n++)
286    {
287	e = FcPatternObjectFindElt (font, FcObjectFromName (os->objects[n]));
288	if (e)
289	    h = h ^ FcListValueListHash (FcPatternEltValues(e));
290    }
291    return h;
292}
293
294typedef struct _FcListBucket {
295    struct _FcListBucket    *next;
296    FcChar32		    hash;
297    FcPattern		    *pattern;
298} FcListBucket;
299
300#define FC_LIST_HASH_SIZE   4099
301
302typedef struct _FcListHashTable {
303    int		    entries;
304    FcListBucket    *buckets[FC_LIST_HASH_SIZE];
305} FcListHashTable;
306
307static void
308FcListHashTableInit (FcListHashTable *table)
309{
310    table->entries = 0;
311    memset (table->buckets, '\0', sizeof (table->buckets));
312}
313
314static void
315FcListHashTableCleanup (FcListHashTable *table)
316{
317    int	i;
318    FcListBucket    *bucket, *next;
319
320    for (i = 0; i < FC_LIST_HASH_SIZE; i++)
321    {
322	for (bucket = table->buckets[i]; bucket; bucket = next)
323	{
324	    next = bucket->next;
325	    FcPatternDestroy (bucket->pattern);
326	    FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
327	    free (bucket);
328	}
329	table->buckets[i] = 0;
330    }
331    table->entries = 0;
332}
333
334static int
335FcGetDefaultObjectLangIndex (FcPattern *font, FcObject object)
336{
337    FcChar8	   *lang = FcGetDefaultLang ();
338    FcPatternElt   *e = FcPatternObjectFindElt (font, object);
339    FcValueListPtr  v;
340    FcValue         value;
341    int             idx = -1;
342    int             i;
343
344    if (e)
345    {
346	for (v = FcPatternEltValues(e), i = 0; v; v = FcValueListNext(v), ++i)
347	{
348	    value = FcValueCanonicalize (&v->value);
349
350	    if (value.type == FcTypeString)
351	    {
352		FcLangResult res = FcLangCompare (value.u.s, lang);
353		if (res == FcLangEqual)
354		    return i;
355
356		if (res == FcLangDifferentCountry && idx < 0)
357		    idx = i;
358	    }
359	}
360    }
361
362    return (idx > 0) ? idx : 0;
363}
364
365static FcBool
366FcListAppend (FcListHashTable	*table,
367	      FcPattern		*font,
368	      FcObjectSet	*os)
369{
370    int		    o;
371    FcPatternElt    *e;
372    FcValueListPtr  v;
373    FcChar32	    hash;
374    FcListBucket    **prev, *bucket;
375    int             familyidx = -1;
376    int             fullnameidx = -1;
377    int             styleidx = -1;
378    int             defidx = 0;
379    int             idx;
380
381    hash = FcListPatternHash (font, os);
382    for (prev = &table->buckets[hash % FC_LIST_HASH_SIZE];
383	 (bucket = *prev); prev = &(bucket->next))
384    {
385	if (bucket->hash == hash &&
386	    FcListPatternEqual (bucket->pattern, font, os))
387	    return FcTrue;
388    }
389    bucket = (FcListBucket *) malloc (sizeof (FcListBucket));
390    if (!bucket)
391	goto bail0;
392    FcMemAlloc (FC_MEM_LISTBUCK, sizeof (FcListBucket));
393    bucket->next = 0;
394    bucket->hash = hash;
395    bucket->pattern = FcPatternCreate ();
396    if (!bucket->pattern)
397	goto bail1;
398
399    for (o = 0; o < os->nobject; o++)
400    {
401	if (!strcmp (os->objects[o], FC_FAMILY) || !strcmp (os->objects[o], FC_FAMILYLANG))
402	{
403	    if (familyidx < 0)
404		familyidx = FcGetDefaultObjectLangIndex (font, FC_FAMILYLANG_OBJECT);
405	    defidx = familyidx;
406	}
407	else if (!strcmp (os->objects[o], FC_FULLNAME) || !strcmp (os->objects[o], FC_FULLNAMELANG))
408	{
409	    if (fullnameidx < 0)
410		fullnameidx = FcGetDefaultObjectLangIndex (font, FC_FULLNAMELANG_OBJECT);
411	    defidx = fullnameidx;
412	}
413	else if (!strcmp (os->objects[o], FC_STYLE) || !strcmp (os->objects[o], FC_STYLELANG))
414	{
415	    if (styleidx < 0)
416		styleidx = FcGetDefaultObjectLangIndex (font, FC_STYLELANG_OBJECT);
417	    defidx = styleidx;
418	}
419	else
420	    defidx = 0;
421
422	e = FcPatternObjectFindElt (font, FcObjectFromName (os->objects[o]));
423	if (e)
424	{
425	    for (v = FcPatternEltValues(e), idx = 0; v;
426		 v = FcValueListNext(v), ++idx)
427	    {
428		if (!FcPatternAdd (bucket->pattern,
429				   os->objects[o],
430				   FcValueCanonicalize(&v->value), defidx != idx))
431		    goto bail2;
432	    }
433	}
434    }
435    *prev = bucket;
436    ++table->entries;
437
438    return FcTrue;
439
440bail2:
441    FcPatternDestroy (bucket->pattern);
442bail1:
443    FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
444    free (bucket);
445bail0:
446    return FcFalse;
447}
448
449FcFontSet *
450FcFontSetList (FcConfig	    *config,
451	       FcFontSet    **sets,
452	       int	    nsets,
453	       FcPattern    *p,
454	       FcObjectSet  *os)
455{
456    FcFontSet	    *ret;
457    FcFontSet	    *s;
458    int		    f;
459    int		    set;
460    FcListHashTable table;
461    int		    i;
462    FcListBucket    *bucket;
463    int             destroy_os = 0;
464
465    if (!config)
466    {
467	if (!FcInitBringUptoDate ())
468	    goto bail0;
469
470	config = FcConfigGetCurrent ();
471	if (!config)
472	    goto bail0;
473    }
474    FcListHashTableInit (&table);
475
476    if (!os)
477    {
478	os = FcObjectGetSet ();
479	destroy_os = 1;
480    }
481
482    /*
483     * Walk all available fonts adding those that
484     * match to the hash table
485     */
486    for (set = 0; set < nsets; set++)
487    {
488	s = sets[set];
489	if (!s)
490	    continue;
491	for (f = 0; f < s->nfont; f++)
492	    if (FcListPatternMatchAny (p,		/* pattern */
493				       s->fonts[f]))	/* font */
494		if (!FcListAppend (&table, s->fonts[f], os))
495		    goto bail1;
496    }
497#if 0
498    {
499	int	max = 0;
500	int	full = 0;
501	int	ents = 0;
502	int	len;
503	for (i = 0; i < FC_LIST_HASH_SIZE; i++)
504	{
505	    if ((bucket = table.buckets[i]))
506	    {
507		len = 0;
508		for (; bucket; bucket = bucket->next)
509		{
510		    ents++;
511		    len++;
512		}
513		if (len > max)
514		    max = len;
515		full++;
516	    }
517	}
518	printf ("used: %d max: %d avg: %g\n", full, max,
519		(double) ents / FC_LIST_HASH_SIZE);
520    }
521#endif
522    /*
523     * Walk the hash table and build
524     * a font set
525     */
526    ret = FcFontSetCreate ();
527    if (!ret)
528	goto bail0;
529    for (i = 0; i < FC_LIST_HASH_SIZE; i++)
530	while ((bucket = table.buckets[i]))
531	{
532	    if (!FcFontSetAdd (ret, bucket->pattern))
533		goto bail2;
534	    table.buckets[i] = bucket->next;
535	    FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
536	    free (bucket);
537	}
538
539    return ret;
540
541bail2:
542    FcFontSetDestroy (ret);
543bail1:
544    FcListHashTableCleanup (&table);
545bail0:
546    if (destroy_os)
547	FcObjectSetDestroy (os);
548    return 0;
549}
550
551FcFontSet *
552FcFontList (FcConfig	*config,
553	    FcPattern	*p,
554	    FcObjectSet *os)
555{
556    FcFontSet	*sets[2];
557    int		nsets;
558
559    if (!config)
560    {
561	if (!FcInitBringUptoDate ())
562	    return 0;
563
564	config = FcConfigGetCurrent ();
565	if (!config)
566	    return 0;
567    }
568    nsets = 0;
569    if (config->fonts[FcSetSystem])
570	sets[nsets++] = config->fonts[FcSetSystem];
571    if (config->fonts[FcSetApplication])
572	sets[nsets++] = config->fonts[FcSetApplication];
573    return FcFontSetList (config, sets, nsets, p, os);
574}
575#define __fclist__
576#include "fcaliastail.h"
577#undef __fclist__
578