Coverage Report

Created: 2026-05-31 06:50

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