Coverage Report

Created: 2025-07-23 08:13

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