fclist.c revision 2c393a42
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 "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 || (res == FcLangDifferentCountry && idx < 0))
354		    idx = i;
355	    }
356	}
357    }
358
359    return (idx > 0) ? idx : 0;
360}
361
362static FcBool
363FcListAppend (FcListHashTable	*table,
364	      FcPattern		*font,
365	      FcObjectSet	*os)
366{
367    int		    o;
368    FcPatternElt    *e;
369    FcValueListPtr  v;
370    FcChar32	    hash;
371    FcListBucket    **prev, *bucket;
372    int             familyidx = -1;
373    int             fullnameidx = -1;
374    int             styleidx = -1;
375    int             defidx = 0;
376    int             idx;
377
378    hash = FcListPatternHash (font, os);
379    for (prev = &table->buckets[hash % FC_LIST_HASH_SIZE];
380	 (bucket = *prev); prev = &(bucket->next))
381    {
382	if (bucket->hash == hash &&
383	    FcListPatternEqual (bucket->pattern, font, os))
384	    return FcTrue;
385    }
386    bucket = (FcListBucket *) malloc (sizeof (FcListBucket));
387    if (!bucket)
388	goto bail0;
389    FcMemAlloc (FC_MEM_LISTBUCK, sizeof (FcListBucket));
390    bucket->next = 0;
391    bucket->hash = hash;
392    bucket->pattern = FcPatternCreate ();
393    if (!bucket->pattern)
394	goto bail1;
395
396    for (o = 0; o < os->nobject; o++)
397    {
398	if (!strcmp (os->objects[o], FC_FAMILY) || !strcmp (os->objects[o], FC_FAMILYLANG))
399	{
400	    if (familyidx < 0)
401		familyidx = FcGetDefaultObjectLangIndex (font, FC_FAMILYLANG_OBJECT);
402	    defidx = familyidx;
403	}
404	else if (!strcmp (os->objects[o], FC_FULLNAME) || !strcmp (os->objects[o], FC_FULLNAMELANG))
405	{
406	    if (fullnameidx < 0)
407		fullnameidx = FcGetDefaultObjectLangIndex (font, FC_FULLNAMELANG_OBJECT);
408	    defidx = fullnameidx;
409	}
410	else if (!strcmp (os->objects[o], FC_STYLE) || !strcmp (os->objects[o], FC_STYLELANG))
411	{
412	    if (styleidx < 0)
413		styleidx = FcGetDefaultObjectLangIndex (font, FC_STYLELANG_OBJECT);
414	    defidx = styleidx;
415	}
416	else
417	    defidx = 0;
418
419	e = FcPatternObjectFindElt (font, FcObjectFromName (os->objects[o]));
420	if (e)
421	{
422	    for (v = FcPatternEltValues(e), idx = 0; v;
423		 v = FcValueListNext(v), ++idx)
424	    {
425		if (!FcPatternAdd (bucket->pattern,
426				   os->objects[o],
427				   FcValueCanonicalize(&v->value), defidx != idx))
428		    goto bail2;
429	    }
430	}
431    }
432    *prev = bucket;
433    ++table->entries;
434
435    return FcTrue;
436
437bail2:
438    FcPatternDestroy (bucket->pattern);
439bail1:
440    FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
441    free (bucket);
442bail0:
443    return FcFalse;
444}
445
446FcFontSet *
447FcFontSetList (FcConfig	    *config,
448	       FcFontSet    **sets,
449	       int	    nsets,
450	       FcPattern    *p,
451	       FcObjectSet  *os)
452{
453    FcFontSet	    *ret;
454    FcFontSet	    *s;
455    int		    f;
456    int		    set;
457    FcListHashTable table;
458    int		    i;
459    FcListBucket    *bucket;
460
461    if (!config)
462    {
463	if (!FcInitBringUptoDate ())
464	    goto bail0;
465
466	config = FcConfigGetCurrent ();
467	if (!config)
468	    goto bail0;
469    }
470    FcListHashTableInit (&table);
471    /*
472     * Walk all available fonts adding those that
473     * match to the hash table
474     */
475    for (set = 0; set < nsets; set++)
476    {
477	s = sets[set];
478	if (!s)
479	    continue;
480	for (f = 0; f < s->nfont; f++)
481	    if (FcListPatternMatchAny (p,		/* pattern */
482				       s->fonts[f]))	/* font */
483		if (!FcListAppend (&table, s->fonts[f], os))
484		    goto bail1;
485    }
486#if 0
487    {
488	int	max = 0;
489	int	full = 0;
490	int	ents = 0;
491	int	len;
492	for (i = 0; i < FC_LIST_HASH_SIZE; i++)
493	{
494	    if ((bucket = table.buckets[i]))
495	    {
496		len = 0;
497		for (; bucket; bucket = bucket->next)
498		{
499		    ents++;
500		    len++;
501		}
502		if (len > max)
503		    max = len;
504		full++;
505	    }
506	}
507	printf ("used: %d max: %d avg: %g\n", full, max,
508		(double) ents / FC_LIST_HASH_SIZE);
509    }
510#endif
511    /*
512     * Walk the hash table and build
513     * a font set
514     */
515    ret = FcFontSetCreate ();
516    if (!ret)
517	goto bail0;
518    for (i = 0; i < FC_LIST_HASH_SIZE; i++)
519	while ((bucket = table.buckets[i]))
520	{
521	    if (!FcFontSetAdd (ret, bucket->pattern))
522		goto bail2;
523	    table.buckets[i] = bucket->next;
524	    FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
525	    free (bucket);
526	}
527
528    return ret;
529
530bail2:
531    FcFontSetDestroy (ret);
532bail1:
533    FcListHashTableCleanup (&table);
534bail0:
535    return 0;
536}
537
538FcFontSet *
539FcFontList (FcConfig	*config,
540	    FcPattern	*p,
541	    FcObjectSet *os)
542{
543    FcFontSet	*sets[2];
544    int		nsets;
545
546    if (!config)
547    {
548	config = FcConfigGetCurrent ();
549	if (!config)
550	    return 0;
551    }
552    nsets = 0;
553    if (config->fonts[FcSetSystem])
554	sets[nsets++] = config->fonts[FcSetSystem];
555    if (config->fonts[FcSetApplication])
556	sets[nsets++] = config->fonts[FcSetApplication];
557    return FcFontSetList (config, sets, nsets, p, os);
558}
559#define __fclist__
560#include "fcaliastail.h"
561#undef __fclist__
562