Coverage Report

Created: 2025-09-27 07:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fontconfig/src/fcmatch.c
Line
Count
Source
1
/*
2
 * fontconfig/src/fcmatch.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
static double
28
FcCompareNumber (const FcValue *value1, const FcValue *value2, FcValue *bestValue)
29
13.1M
{
30
13.1M
    double v1, v2, v;
31
32
13.1M
    switch ((int)value1->type) {
33
13.1M
    case FcTypeInteger:
34
13.1M
  v1 = (double)value1->u.i;
35
13.1M
  break;
36
0
    case FcTypeDouble:
37
0
  v1 = value1->u.d;
38
0
  break;
39
0
    default:
40
0
  return -1.0;
41
13.1M
    }
42
13.1M
    switch ((int)value2->type) {
43
13.1M
    case FcTypeInteger:
44
13.1M
  v2 = (double)value2->u.i;
45
13.1M
  break;
46
0
    case FcTypeDouble:
47
0
  v2 = value2->u.d;
48
0
  break;
49
0
    default:
50
0
  return -1.0;
51
13.1M
    }
52
13.1M
    v = v2 - v1;
53
13.1M
    if (v < 0)
54
4.43M
  v = -v;
55
13.1M
    *bestValue = FcValueCanonicalize (value2);
56
13.1M
    return v;
57
13.1M
}
58
59
static double
60
FcCompareString (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
61
82.2k
{
62
82.2k
    *bestValue = FcValueCanonicalize (v2);
63
82.2k
    return (double)FcStrCmpIgnoreCase (FcValueString (v1), FcValueString (v2)) != 0;
64
82.2k
}
65
66
static double
67
FcCompareFamily (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
68
0
{
69
    /* rely on the guarantee in FcPatternObjectAddWithBinding that
70
     * families are always FcTypeString. */
71
0
    const FcChar8 *v1_string = FcValueString (v1);
72
0
    const FcChar8 *v2_string = FcValueString (v2);
73
74
0
    *bestValue = FcValueCanonicalize (v2);
75
76
0
    if (FcToLower (*v1_string) != FcToLower (*v2_string) &&
77
0
        *v1_string != ' ' && *v2_string != ' ')
78
0
  return 1.0;
79
80
0
    return (double)FcStrCmpIgnoreBlanksAndCase (v1_string, v2_string) != 0;
81
0
}
82
83
static double
84
FcComparePostScript (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
85
0
{
86
0
    const FcChar8 *v1_string = FcValueString (v1);
87
0
    const FcChar8 *v2_string = FcValueString (v2);
88
0
    int            n;
89
0
    size_t         len1, len2, mlen;
90
91
0
    *bestValue = FcValueCanonicalize (v2);
92
93
0
    if (FcToLower (*v1_string) != FcToLower (*v2_string) &&
94
0
        *v1_string != ' ' && *v2_string != ' ')
95
0
  return 1.0;
96
97
0
    n = FcStrMatchIgnoreCaseAndDelims (v1_string, v2_string, (const FcChar8 *)" -,");
98
0
    len1 = strlen ((const char *)v1_string);
99
0
    len2 = strlen ((const char *)v2_string);
100
0
    mlen = FC_MAX (len1, len2);
101
102
0
    return (double)(mlen - n) / (double)mlen;
103
0
}
104
105
static double
106
FcCompareLang (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
107
13.1M
{
108
13.1M
    FcLangResult result;
109
110
13.1M
    switch ((int)v1->type) {
111
0
    case FcTypeLangSet:
112
0
  switch ((int)v2->type) {
113
0
  case FcTypeLangSet:
114
0
      result = FcLangSetCompare (FcValueLangSet (v1), FcValueLangSet (v2));
115
0
      break;
116
0
  case FcTypeString:
117
0
      result = FcLangSetHasLang (FcValueLangSet (v1), FcValueString (v2));
118
0
      break;
119
0
  default:
120
0
      return -1.0;
121
0
  }
122
0
  break;
123
13.1M
    case FcTypeString:
124
13.1M
  switch ((int)v2->type) {
125
13.1M
  case FcTypeLangSet:
126
13.1M
      result = FcLangSetHasLang (FcValueLangSet (v2), FcValueString (v1));
127
13.1M
      break;
128
0
  case FcTypeString:
129
0
      result = FcLangCompare (FcValueString (v1), FcValueString (v2));
130
0
      break;
131
0
  default:
132
0
      return -1.0;
133
13.1M
  }
134
13.1M
  break;
135
13.1M
    default:
136
0
  return -1.0;
137
13.1M
    }
138
13.1M
    *bestValue = FcValueCanonicalize (v2);
139
13.1M
    switch (result) {
140
4.56M
    case FcLangEqual:
141
4.56M
  return 0;
142
0
    case FcLangDifferentCountry:
143
0
  return 1;
144
8.61M
    case FcLangDifferentLang:
145
8.61M
    default:
146
8.61M
  return 2;
147
13.1M
    }
148
13.1M
}
149
150
static double
151
FcCompareBool (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
152
13.1M
{
153
13.1M
    if (v2->type != FcTypeBool || v1->type != FcTypeBool)
154
0
  return -1.0;
155
156
13.1M
    bestValue->type = FcTypeBool;
157
13.1M
    if (v2->u.b != FcDontCare)
158
13.1M
  bestValue->u.b = v2->u.b;
159
0
    else
160
0
  bestValue->u.b = v1->u.b;
161
162
13.1M
    return (double)((v2->u.b ^ v1->u.b) == 1);
163
13.1M
}
164
165
static double
166
FcCompareCharSet (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
167
0
{
168
0
    *bestValue = FcValueCanonicalize (v2); /* TODO Improve. */
169
0
    return (double)FcCharSetSubtractCount (FcValueCharSet (v1), FcValueCharSet (v2));
170
0
}
171
172
static double
173
FcCompareRange (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
174
8.73M
{
175
8.73M
    FcValue value1 = FcValueCanonicalize (v1);
176
8.73M
    FcValue value2 = FcValueCanonicalize (v2);
177
8.73M
    double  b1, e1, b2, e2, d;
178
179
8.73M
    switch ((int)value1.type) {
180
8.73M
    case FcTypeInteger:
181
8.73M
  b1 = e1 = value1.u.i;
182
8.73M
  break;
183
0
    case FcTypeDouble:
184
0
  b1 = e1 = value1.u.d;
185
0
  break;
186
0
    case FcTypeRange:
187
0
  b1 = value1.u.r->begin;
188
0
  e1 = value1.u.r->end;
189
0
  break;
190
0
    default:
191
0
  return -1;
192
8.73M
    }
193
8.73M
    switch ((int)value2.type) {
194
0
    case FcTypeInteger:
195
0
  b2 = e2 = value2.u.i;
196
0
  break;
197
8.73M
    case FcTypeDouble:
198
8.73M
  b2 = e2 = value2.u.d;
199
8.73M
  break;
200
0
    case FcTypeRange:
201
0
  b2 = value2.u.r->begin;
202
0
  e2 = value2.u.r->end;
203
0
  break;
204
0
    default:
205
0
  return -1;
206
8.73M
    }
207
208
8.73M
    if (e1 < b2)
209
1.83M
  d = b2;
210
6.90M
    else if (e2 < b1)
211
1.97M
  d = e2;
212
4.92M
    else
213
4.92M
  d = (FC_MAX (b1, b2) + FC_MIN (e1, e2)) * .5;
214
215
8.73M
    bestValue->type = FcTypeDouble;
216
8.73M
    bestValue->u.d = d;
217
218
    /* If the ranges overlap, it's a match, otherwise return closest distance. */
219
8.73M
    if (e1 < b2 || e2 < b1)
220
3.80M
  return FC_MIN (fabs (b2 - e1), fabs (b1 - e2));
221
4.92M
    else
222
4.92M
  return 0.0;
223
8.73M
}
224
225
static double
226
FcCompareSize (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
227
0
{
228
0
    FcValue value1 = FcValueCanonicalize (v1);
229
0
    FcValue value2 = FcValueCanonicalize (v2);
230
0
    double  b1, e1, b2, e2;
231
232
0
    switch ((int)value1.type) {
233
0
    case FcTypeInteger:
234
0
  b1 = e1 = value1.u.i;
235
0
  break;
236
0
    case FcTypeDouble:
237
0
  b1 = e1 = value1.u.d;
238
0
  break;
239
0
    case FcTypeRange:
240
0
  b1 = value1.u.r->begin;
241
0
  e1 = value1.u.r->end;
242
0
  break;
243
0
    default:
244
0
  return -1;
245
0
    }
246
0
    switch ((int)value2.type) {
247
0
    case FcTypeInteger:
248
0
  b2 = e2 = value2.u.i;
249
0
  break;
250
0
    case FcTypeDouble:
251
0
  b2 = e2 = value2.u.d;
252
0
  break;
253
0
    case FcTypeRange:
254
0
  b2 = value2.u.r->begin;
255
0
  e2 = value2.u.r->end;
256
0
  break;
257
0
    default:
258
0
  return -1;
259
0
    }
260
261
0
    bestValue->type = FcTypeDouble;
262
0
    bestValue->u.d = (b1 + e1) * .5;
263
264
    /* If the ranges overlap, it's a match, otherwise return closest distance. */
265
0
    if (e1 < b2 || e2 < b1)
266
0
  return FC_MIN (fabs (b2 - e1), fabs (b1 - e2));
267
0
    if (b2 != e2 && b1 == e2) /* Semi-closed interval. */
268
0
  return 1e-15;
269
0
    else
270
0
  return 0.0;
271
0
}
272
273
static double
274
FcCompareFilename (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
275
0
{
276
0
    const FcChar8 *s1 = FcValueString (v1), *s2 = FcValueString (v2);
277
0
    *bestValue = FcValueCanonicalize (v2);
278
0
    if (FcStrCmp (s1, s2) == 0)
279
0
  return 0.0;
280
0
    else if (FcStrCmpIgnoreCase (s1, s2) == 0)
281
0
  return 1.0;
282
0
    else if (FcStrGlobMatch (s1, s2))
283
0
  return 2.0;
284
0
    else
285
0
  return 3.0;
286
0
}
287
288
/* Define priorities to -1 for objects that don't have a compare function. */
289
290
#define PRI_NULL(n)        \
291
    PRI_##n##_STRONG = -1, \
292
    PRI_##n##_WEAK = -1,
293
#define PRI1(n)
294
#define PRI_FcCompareFamily(n)     PRI1 (n)
295
#define PRI_FcCompareString(n)     PRI1 (n)
296
#define PRI_FcCompareNumber(n)     PRI1 (n)
297
#define PRI_FcCompareBool(n)       PRI1 (n)
298
#define PRI_FcCompareFilename(n)   PRI1 (n)
299
#define PRI_FcCompareCharSet(n)    PRI1 (n)
300
#define PRI_FcCompareLang(n)       PRI1 (n)
301
#define PRI_FcComparePostScript(n) PRI1 (n)
302
#define PRI_FcCompareRange(n)      PRI1 (n)
303
#define PRI_FcCompareSize(n)       PRI1 (n)
304
305
#define FC_OBJECT(NAME, Type, Cmp) PRI_##Cmp (NAME)
306
307
typedef enum _FcMatcherPriorityDummy {
308
#include "fcobjs.h"
309
} FcMatcherPriorityDummy;
310
311
#undef FC_OBJECT
312
313
/* Canonical match priority order. */
314
315
#undef PRI1
316
#define PRI1(n)                     \
317
    PRI_##n,                        \
318
  PRI_##n##_STRONG = PRI_##n, \
319
  PRI_##n##_WEAK = PRI_##n
320
321
typedef enum _FcMatcherPriority {
322
    PRI1 (FILE),
323
    PRI1 (FONT_WRAPPER),
324
    PRI1 (FONTFORMAT),
325
    PRI1 (VARIABLE),
326
    PRI1 (NAMED_INSTANCE),
327
    PRI1 (SCALABLE),
328
    PRI1 (COLOR),
329
    PRI1 (FOUNDRY),
330
    PRI1 (CHARSET),
331
    PRI1 (GENERIC_FAMILY),
332
    PRI_FAMILY_STRONG,
333
    PRI_POSTSCRIPT_NAME_STRONG,
334
    PRI1 (LANG),
335
    PRI_FAMILY_WEAK,
336
    PRI_POSTSCRIPT_NAME_WEAK,
337
    PRI1 (SYMBOL),
338
    PRI1 (SPACING),
339
    PRI1 (SIZE),
340
    PRI1 (PIXEL_SIZE),
341
    PRI1 (STYLE),
342
    PRI1 (SLANT),
343
    PRI1 (WEIGHT),
344
    PRI1 (WIDTH),
345
    PRI1 (FONT_HAS_HINT),
346
    PRI1 (DECORATIVE),
347
    PRI1 (ANTIALIAS),
348
    PRI1 (RASTERIZER),
349
    PRI1 (OUTLINE),
350
    PRI1 (ORDER),
351
    PRI1 (FONTVERSION),
352
    PRI_END
353
} FcMatcherPriority;
354
355
#undef PRI1
356
357
typedef struct _FcMatcher {
358
    FcObject object;
359
    double (*compare) (const FcValue *v1, const FcValue *v2, FcValue *bestValue);
360
    int strong, weak;
361
} FcMatcher;
362
363
/*
364
 * Order is significant, it defines the precedence of
365
 * each value, earlier values are more significant than
366
 * later values
367
 */
368
#define FC_OBJECT(NAME, Type, Cmp) { FC_##NAME##_OBJECT, Cmp, PRI_##NAME##_STRONG, PRI_##NAME##_WEAK },
369
static const FcMatcher _FcMatchers[] = {
370
    { FC_INVALID_OBJECT, NULL, -1, -1 },
371
#include "fcobjs.h"
372
};
373
#undef FC_OBJECT
374
375
static const FcMatcher *
376
FcObjectToMatcher (FcObject object,
377
                   FcBool   include_lang)
378
52.5M
{
379
52.5M
    if (include_lang) {
380
0
  switch (object) {
381
0
  case FC_FAMILYLANG_OBJECT:
382
0
  case FC_STYLELANG_OBJECT:
383
0
  case FC_FULLNAMELANG_OBJECT:
384
0
      object = FC_LANG_OBJECT;
385
0
      break;
386
0
  }
387
0
    }
388
52.5M
    if (object > FC_MAX_BASE_OBJECT ||
389
52.5M
        !_FcMatchers[object].compare ||
390
39.4M
        _FcMatchers[object].strong == -1 ||
391
39.4M
        _FcMatchers[object].weak == -1)
392
13.1M
  return NULL;
393
394
39.4M
    return _FcMatchers + object;
395
52.5M
}
396
397
static FcBool
398
FcCompareValueList (FcObject         object,
399
                    const FcMatcher *match,
400
                    FcValueListPtr   v1orig, /* pattern */
401
                    FcValueListPtr   v2orig, /* target */
402
                    FcValue         *bestValue,
403
                    double          *value,
404
                    int             *n,
405
                    FcResult        *result)
406
52.5M
{
407
52.5M
    FcValueListPtr v1, v2;
408
52.5M
    double         v, best, bestStrong, bestWeak;
409
52.5M
    int            j, k, pos = 0;
410
52.5M
    int            weak, strong;
411
412
52.5M
    if (!match) {
413
13.1M
  if (bestValue)
414
0
      *bestValue = FcValueCanonicalize (&v2orig->value);
415
13.1M
  if (n)
416
0
      *n = 0;
417
13.1M
  return FcTrue;
418
13.1M
    }
419
420
39.4M
    weak = match->weak;
421
39.4M
    strong = match->strong;
422
423
39.4M
    best = 1e99;
424
39.4M
    bestStrong = 1e99;
425
39.4M
    bestWeak = 1e99;
426
53.9M
    for (v1 = v1orig, j = 0; v1; v1 = FcValueListNext (v1), j++) {
427
58.3M
  for (v2 = v2orig, k = 0; v2; v2 = FcValueListNext (v2), k++) {
428
43.7M
      FcValue matchValue;
429
43.7M
      v = (match->compare) (&v1->value, &v2->value, &matchValue);
430
43.7M
      if (v < 0) {
431
0
    *result = FcResultTypeMismatch;
432
0
    return FcFalse;
433
0
      }
434
43.7M
      v = v * 1000 + j * 100 + k * (v2->value.type == FcTypeString ? 1 : 0);
435
43.7M
      if (v < best) {
436
43.7M
    if (bestValue)
437
0
        *bestValue = matchValue;
438
43.7M
    best = v;
439
43.7M
    pos = k;
440
43.7M
      }
441
43.7M
      if (weak == strong) {
442
    /* found the best possible match */
443
43.7M
    if (best < 1000)
444
29.1M
        goto done;
445
43.7M
      } else if (v1->binding == FcValueBindingStrong) {
446
0
    if (v < bestStrong)
447
0
        bestStrong = v;
448
0
      } else {
449
0
    if (v < bestWeak)
450
0
        bestWeak = v;
451
0
      }
452
43.7M
  }
453
43.7M
    }
454
39.4M
done:
455
39.4M
    if (FcDebug() & FC_DBG_MATCHV) {
456
0
  printf (" %s: %g ", FcObjectName (object), best);
457
0
  FcValueListPrint (v1orig);
458
0
  printf (", ");
459
0
  FcValueListPrint (v2orig);
460
0
  printf ("\n");
461
0
    }
462
39.4M
    if (value) {
463
39.4M
  if (weak == strong)
464
39.4M
      value[strong] += best;
465
0
  else {
466
0
      value[weak] += bestWeak;
467
0
      value[strong] += bestStrong;
468
0
  }
469
39.4M
    }
470
39.4M
    if (n)
471
0
  *n = pos;
472
473
39.4M
    return FcTrue;
474
39.4M
}
475
476
/* The bulk of the time in FcFontMatch and FcFontSort goes to
477
 * walking long lists of family names. We speed this up with a
478
 * hash table.
479
 */
480
typedef struct
481
{
482
    double strong_value;
483
    double weak_value;
484
} FamilyEntry;
485
486
typedef struct
487
{
488
    FcHashTable *family_hash;
489
} FcCompareData;
490
491
static void
492
FcCompareDataClear (FcCompareData *data)
493
198k
{
494
198k
    FcHashTableDestroy (data->family_hash);
495
198k
}
496
497
static void
498
FcCompareDataInit (FcPattern     *pat,
499
                   FcCompareData *data)
500
198k
{
501
198k
    FcHashTable   *table;
502
198k
    FcPatternElt  *elt;
503
198k
    FcValueListPtr l;
504
198k
    int            i;
505
198k
    const void    *key;
506
198k
    FamilyEntry   *e;
507
508
198k
    table = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreBlanksAndCase,
509
198k
                               (FcCompareFunc)FcStrCmpIgnoreBlanksAndCase,
510
198k
                               NULL,
511
198k
                               NULL,
512
198k
                               NULL,
513
198k
                               free);
514
515
198k
    elt = FcPatternObjectFindElt (pat, FC_FAMILY_OBJECT);
516
198k
    if (elt) {
517
397k
  for (l = FcPatternEltValues (elt), i = 0; l; l = FcValueListNext (l), i++) {
518
198k
      key = FcValueString (&l->value);
519
198k
      if (!FcHashTableFind (table, key, (void **)&e)) {
520
198k
    e = malloc (sizeof (FamilyEntry));
521
198k
    e->strong_value = 1e99;
522
198k
    e->weak_value = 1e99;
523
198k
    FcHashTableAdd (table, (void *)key, e);
524
198k
      }
525
198k
      if (l->binding == FcValueBindingWeak) {
526
0
    if (i < e->weak_value)
527
0
        e->weak_value = i;
528
198k
      } else {
529
198k
    if (i < e->strong_value)
530
198k
        e->strong_value = i;
531
198k
      }
532
198k
  }
533
198k
    }
534
535
198k
    data->family_hash = table;
536
198k
}
537
538
static FcBool
539
FcCompareFamilies (FcPattern     *pat,
540
                   FcValueListPtr v1orig,
541
                   FcPattern     *fnt,
542
                   FcValueListPtr v2orig,
543
                   double        *value,
544
                   FcResult      *result,
545
                   FcHashTable   *table)
546
4.36M
{
547
4.36M
    FcValueListPtr v2;
548
4.36M
    double         strong_value;
549
4.36M
    double         weak_value;
550
4.36M
    const void    *key;
551
4.36M
    FamilyEntry   *e;
552
553
4.36M
    assert (table != NULL);
554
555
4.36M
    strong_value = 1e99;
556
4.36M
    weak_value = 1e99;
557
558
10.5M
    for (v2 = v2orig; v2; v2 = FcValueListNext (v2)) {
559
6.15M
  key = FcValueString (&v2->value);
560
6.15M
  if (FcHashTableFind (table, key, (void **)&e)) {
561
848
      if (e->strong_value < strong_value)
562
848
    strong_value = e->strong_value;
563
848
      if (e->weak_value < weak_value)
564
0
    weak_value = e->weak_value;
565
848
  }
566
6.15M
    }
567
4.36M
    if (FcDebug() & FC_DBG_MATCHV) {
568
0
  printf ("%s: %g (%g) ", FcObjectName (FC_FAMILY_OBJECT), strong_value, weak_value);
569
0
  FcValueListPrint (v1orig);
570
0
  printf (", ");
571
0
  FcValueListPrint (v2orig);
572
0
  printf ("\n");
573
0
    }
574
575
4.36M
    value[PRI_FAMILY_STRONG] = strong_value;
576
4.36M
    value[PRI_FAMILY_WEAK] = weak_value;
577
578
4.36M
    return FcTrue;
579
4.36M
}
580
581
/*
582
 * Return a value indicating the distance between the two lists of
583
 * values
584
 */
585
586
static FcBool
587
FcCompare (FcPattern     *pat,
588
           FcPattern     *fnt,
589
           double        *value,
590
           FcResult      *result,
591
           FcCompareData *data)
592
4.36M
{
593
4.36M
    int i, i1, i2;
594
595
135M
    for (i = 0; i < PRI_END; i++)
596
131M
  value[i] = 0.0;
597
598
4.36M
    i1 = 0;
599
4.36M
    i2 = 0;
600
171M
    while (i1 < pat->num && i2 < fnt->num) {
601
167M
  FcPatternElt *elt_i1 = &FcPatternElts (pat)[i1];
602
167M
  FcPatternElt *elt_i2 = &FcPatternElts (fnt)[i2];
603
604
167M
  i = FcObjectCompare (elt_i1->object, elt_i2->object);
605
167M
  if (i > 0)
606
57.4M
      i2++;
607
109M
  else if (i < 0)
608
52.6M
      i1++;
609
56.9M
  else if (elt_i1->object == FC_FAMILY_OBJECT && data->family_hash) {
610
4.36M
      if (!FcCompareFamilies (pat, FcPatternEltValues (elt_i1),
611
4.36M
                              fnt, FcPatternEltValues (elt_i2),
612
4.36M
                              value, result,
613
4.36M
                              data->family_hash))
614
0
    return FcFalse;
615
4.36M
      i1++;
616
4.36M
      i2++;
617
52.5M
  } else {
618
52.5M
      const FcMatcher *match = FcObjectToMatcher (elt_i1->object, FcFalse);
619
52.5M
      if (!FcCompareValueList (elt_i1->object, match,
620
52.5M
                               FcPatternEltValues (elt_i1),
621
52.5M
                               FcPatternEltValues (elt_i2),
622
52.5M
                               NULL, value, NULL, result))
623
0
    return FcFalse;
624
52.5M
      i1++;
625
52.5M
      i2++;
626
52.5M
  }
627
167M
    }
628
4.36M
    return FcTrue;
629
4.36M
}
630
631
FcPattern *
632
FcFontRenderPrepare (FcConfig  *config,
633
                     FcPattern *pat,
634
                     FcPattern *font)
635
0
{
636
0
    FcPattern    *newp;
637
0
    int           i;
638
0
    FcPatternElt *fe, *pe;
639
0
    FcValue       v;
640
0
    FcResult      result;
641
0
    FcBool        variable = FcFalse;
642
0
    FcStrBuf      variations;
643
644
0
    assert (pat != NULL);
645
0
    assert (font != NULL);
646
647
0
    FcPatternObjectGetBool (font, FC_VARIABLE_OBJECT, 0, &variable);
648
0
    assert (variable != FcDontCare);
649
0
    if (variable)
650
0
  FcStrBufInit (&variations, NULL, 0);
651
652
0
    newp = FcPatternCreate();
653
0
    if (!newp)
654
0
  return NULL;
655
0
    for (i = 0; i < font->num; i++) {
656
0
  fe = &FcPatternElts (font)[i];
657
0
  if (fe->object == FC_FAMILYLANG_OBJECT ||
658
0
      fe->object == FC_STYLELANG_OBJECT ||
659
0
      fe->object == FC_FULLNAMELANG_OBJECT) {
660
      /* ignore those objects. we need to deal with them
661
       * another way */
662
0
      continue;
663
0
  }
664
0
  if (fe->object == FC_FAMILY_OBJECT ||
665
0
      fe->object == FC_STYLE_OBJECT ||
666
0
      fe->object == FC_FULLNAME_OBJECT) {
667
0
      FcPatternElt *fel, *pel;
668
669
0
      FC_ASSERT_STATIC ((FC_FAMILY_OBJECT + 1) == FC_FAMILYLANG_OBJECT);
670
0
      FC_ASSERT_STATIC ((FC_STYLE_OBJECT + 1) == FC_STYLELANG_OBJECT);
671
0
      FC_ASSERT_STATIC ((FC_FULLNAME_OBJECT + 1) == FC_FULLNAMELANG_OBJECT);
672
673
0
      fel = FcPatternObjectFindElt (font, fe->object + 1);
674
0
      pel = FcPatternObjectFindElt (pat, fe->object + 1);
675
676
0
      if (fel && pel) {
677
    /* The font has name languages, and pattern asks for specific language(s).
678
     * Match on language and and prefer that result.
679
     * Note:  Currently the code only give priority to first matching language.
680
     */
681
0
    int              n = 1, j;
682
0
    FcValueListPtr   l1, l2, ln = NULL, ll = NULL;
683
0
    const FcMatcher *match = FcObjectToMatcher (pel->object, FcTrue);
684
685
0
    if (!FcCompareValueList (pel->object, match,
686
0
                             FcPatternEltValues (pel),
687
0
                             FcPatternEltValues (fel), NULL, NULL, &n, &result)) {
688
0
        FcPatternDestroy (newp);
689
0
        return NULL;
690
0
    }
691
692
0
    for (j = 0, l1 = FcPatternEltValues (fe), l2 = FcPatternEltValues (fel);
693
0
         l1 != NULL || l2 != NULL;
694
0
         j++, l1 = l1 ? FcValueListNext (l1) : NULL, l2 = l2 ? FcValueListNext (l2) : NULL) {
695
0
        FcValueListPtr (*func) (FcValueListPtr, FcValue, FcValueBinding);
696
0
        FcValueBinding binding = FcValueBindingEnd;
697
698
0
        if (j == n) {
699
0
      binding = FcValueBindingStrong;
700
0
      func = FcValueListPrepend;
701
0
        } else
702
0
      func = FcValueListAppend;
703
0
        if (l1) {
704
0
      ln = func (ln,
705
0
                 FcValueCanonicalize (&l1->value),
706
0
                 l1->binding);
707
0
        }
708
0
        if (l2) {
709
0
      if (binding == FcValueBindingEnd)
710
0
          binding = l2->binding;
711
0
      ll = func (ll,
712
0
                 FcValueCanonicalize (&l2->value),
713
0
                 binding);
714
0
        }
715
0
    }
716
0
    FcPatternObjectListAdd (newp, fe->object, ln, FcFalse);
717
0
    FcPatternObjectListAdd (newp, fel->object, ll, FcFalse);
718
719
0
    continue;
720
0
      } else if (fel) {
721
    /* Pattern doesn't ask for specific language.  Copy all for name and
722
     * lang. */
723
0
    FcValueListPtr l1, l2;
724
725
0
    l1 = FcValueListDuplicate (FcPatternEltValues (fe));
726
0
    l2 = FcValueListDuplicate (FcPatternEltValues (fel));
727
0
    FcPatternObjectListAdd (newp, fe->object, l1, FcFalse);
728
0
    FcPatternObjectListAdd (newp, fel->object, l2, FcFalse);
729
730
0
    continue;
731
0
      }
732
0
  }
733
734
0
  pe = FcPatternObjectFindElt (pat, fe->object);
735
0
  if (pe) {
736
0
      const FcMatcher *match = FcObjectToMatcher (pe->object, FcFalse);
737
0
      if (!FcCompareValueList (pe->object, match,
738
0
                               FcPatternEltValues (pe),
739
0
                               FcPatternEltValues (fe), &v, NULL, NULL, &result)) {
740
0
    FcPatternDestroy (newp);
741
0
    return NULL;
742
0
      }
743
0
      FcPatternObjectAdd (newp, fe->object, v, FcFalse);
744
745
      /* Set font-variations settings for standard axes in variable fonts. */
746
0
      if (variable &&
747
0
          FcPatternEltValues (fe)->value.type == FcTypeRange &&
748
0
          (fe->object == FC_WEIGHT_OBJECT ||
749
0
           fe->object == FC_WIDTH_OBJECT ||
750
0
           fe->object == FC_SIZE_OBJECT)) {
751
0
    double      num;
752
0
    FcChar8     temp[128];
753
0
    const char *tag = "    ";
754
0
    assert (v.type == FcTypeDouble);
755
0
    num = v.u.d;
756
0
    if (variations.len)
757
0
        FcStrBufChar (&variations, ',');
758
0
    switch (fe->object) {
759
0
    case FC_WEIGHT_OBJECT:
760
0
        tag = "wght";
761
0
        num = FcWeightToOpenType (num);
762
0
        break;
763
764
0
    case FC_WIDTH_OBJECT:
765
0
        tag = "wdth";
766
0
        break;
767
768
0
    case FC_SIZE_OBJECT:
769
0
        tag = "opsz";
770
0
        break;
771
0
    }
772
0
    sprintf ((char *)temp, "%4s=%g", tag, num);
773
0
    FcStrBufString (&variations, temp);
774
0
      }
775
0
  } else {
776
0
      FcPatternObjectListAdd (newp, fe->object,
777
0
                              FcValueListDuplicate (FcPatternEltValues (fe)),
778
0
                              FcTrue);
779
0
  }
780
0
    }
781
0
    for (i = 0; i < pat->num; i++) {
782
0
  pe = &FcPatternElts (pat)[i];
783
0
  fe = FcPatternObjectFindElt (font, pe->object);
784
0
  if (!fe &&
785
0
      pe->object != FC_FAMILYLANG_OBJECT &&
786
0
      pe->object != FC_STYLELANG_OBJECT &&
787
0
      pe->object != FC_FULLNAMELANG_OBJECT) {
788
0
      FcPatternObjectListAdd (newp, pe->object,
789
0
                              FcValueListDuplicate (FcPatternEltValues (pe)),
790
0
                              FcFalse);
791
0
  }
792
0
    }
793
794
0
    if (variable && variations.len) {
795
0
  FcChar8 *vars = NULL;
796
0
  if (FcPatternObjectGetString (newp, FC_FONT_VARIATIONS_OBJECT, 0, &vars) == FcResultMatch) {
797
0
      FcStrBufChar (&variations, ',');
798
0
      FcStrBufString (&variations, vars);
799
0
      FcPatternObjectDel (newp, FC_FONT_VARIATIONS_OBJECT);
800
0
  }
801
802
0
  FcPatternObjectAddString (newp, FC_FONT_VARIATIONS_OBJECT, FcStrBufDoneStatic (&variations));
803
0
  FcStrBufDestroy (&variations);
804
0
    }
805
806
0
    FcConfigSubstituteWithPat (config, newp, pat, FcMatchFont);
807
0
    return newp;
808
0
}
809
810
static FcPattern *
811
FcFontSetMatchInternal (FcFontSet **sets,
812
                        int         nsets,
813
                        FcPattern  *p,
814
                        FcResult   *result)
815
0
{
816
0
    double              score[PRI_END], bestscore[PRI_END];
817
0
    int                 f;
818
0
    FcFontSet          *s;
819
0
    FcPattern          *best, *pat = NULL;
820
0
    int                 i;
821
0
    int                 set;
822
0
    FcCompareData       data;
823
0
    const FcPatternElt *elt;
824
825
0
    for (i = 0; i < PRI_END; i++)
826
0
  bestscore[i] = 0;
827
0
    best = 0;
828
0
    if (FcDebug() & FC_DBG_MATCH) {
829
0
  printf ("Match ");
830
0
  FcPatternPrint (p);
831
0
    }
832
833
0
    FcCompareDataInit (p, &data);
834
835
0
    for (set = 0; set < nsets; set++) {
836
0
  s = sets[set];
837
0
  if (!s)
838
0
      continue;
839
0
  for (f = 0; f < s->nfont; f++) {
840
0
      if (FcDebug() & FC_DBG_MATCHV) {
841
0
    printf ("Font %d ", f);
842
0
    FcPatternPrint (s->fonts[f]);
843
0
      }
844
0
      if (!FcCompare (p, s->fonts[f], score, result, &data)) {
845
0
    FcCompareDataClear (&data);
846
0
    return 0;
847
0
      }
848
0
      if (FcDebug() & FC_DBG_MATCHV) {
849
0
    printf ("Score");
850
0
    for (i = 0; i < PRI_END; i++) {
851
0
        printf (" %g", score[i]);
852
0
    }
853
0
    printf ("\n");
854
0
      }
855
0
      for (i = 0; i < PRI_END; i++) {
856
0
    if (best && bestscore[i] < score[i])
857
0
        break;
858
0
    if (!best || score[i] < bestscore[i]) {
859
0
        for (i = 0; i < PRI_END; i++)
860
0
      bestscore[i] = score[i];
861
0
        best = s->fonts[f];
862
0
        break;
863
0
    }
864
0
      }
865
0
  }
866
0
    }
867
868
0
    FcCompareDataClear (&data);
869
870
    /* Update the binding according to the score to indicate how exactly values matches on. */
871
0
    if (best) {
872
0
  pat = FcPatternCreate();
873
0
  elt = FcPatternElts (best);
874
0
  for (i = 0; i < FcPatternObjectCount (best); i++) {
875
0
      const FcMatcher *match = FcObjectToMatcher (elt[i].object, FcFalse);
876
0
      FcValueListPtr   l = FcPatternEltValues (&elt[i]);
877
878
0
      if (!match)
879
0
    FcPatternObjectListAdd (pat, elt[i].object,
880
0
                            FcValueListDuplicate (l), FcTrue);
881
0
      else {
882
0
    FcValueBinding binding = FcValueBindingWeak;
883
0
    FcValueListPtr newp = NULL, ll, t = NULL;
884
0
    FcValue        v;
885
886
    /* If the value was matched exactly, update the binding to Strong. */
887
0
    if (bestscore[match->strong] < 1000)
888
0
        binding = FcValueBindingStrong;
889
890
0
    for (ll = l; ll != NULL; ll = FcValueListNext (ll)) {
891
0
        if (!newp) {
892
0
      t = newp = FcValueListCreate();
893
0
        } else {
894
0
      t->next = FcValueListCreate();
895
0
      t = FcValueListNext (t);
896
0
        }
897
0
        v = FcValueCanonicalize (&ll->value);
898
0
        t->value = FcValueSave (v);
899
0
        t->binding = binding;
900
0
        t->next = NULL;
901
0
    }
902
0
    FcPatternObjectListAdd (pat, elt[i].object, newp, FcTrue);
903
0
      }
904
0
  }
905
0
    }
906
0
    if (FcDebug() & FC_DBG_MATCH) {
907
0
  printf ("Best score");
908
0
  for (i = 0; i < PRI_END; i++)
909
0
      printf (" %g", bestscore[i]);
910
0
  printf ("\n");
911
0
  FcPatternPrint (pat);
912
0
    }
913
0
    if (FcDebug() & FC_DBG_MATCH2) {
914
0
  char        *env = getenv ("FC_DBG_MATCH_FILTER");
915
0
  FcObjectSet *os = NULL;
916
917
0
  if (env) {
918
0
      char  *ss, *s;
919
0
      char  *p;
920
0
      FcBool f = FcTrue;
921
922
0
      ss = s = strdup (env);
923
0
      os = FcObjectSetCreate();
924
0
      while (f) {
925
0
    size_t len;
926
0
    char  *x;
927
928
0
    if (!(p = strchr (s, ','))) {
929
0
        f = FcFalse;
930
0
        len = strlen (s);
931
0
    } else {
932
0
        len = (p - s);
933
0
    }
934
0
    x = malloc (sizeof (char) * (len + 1));
935
0
    if (x) {
936
0
        strcpy (x, s);
937
0
        if (FcObjectFromName (x) > 0)
938
0
      FcObjectSetAdd (os, x);
939
0
        s = p + 1;
940
0
        free (x);
941
0
    }
942
0
      }
943
0
      free (ss);
944
0
  }
945
0
  FcPatternPrint2 (p, pat, os);
946
0
  if (os)
947
0
      FcObjectSetDestroy (os);
948
0
    }
949
    /* assuming that 'result' is initialized with FcResultNoMatch
950
     * outside this function */
951
0
    if (pat)
952
0
  *result = FcResultMatch;
953
954
0
    return pat;
955
0
}
956
957
FcPattern *
958
FcFontSetMatch (FcConfig   *config,
959
                FcFontSet **sets,
960
                int         nsets,
961
                FcPattern  *p,
962
                FcResult   *result)
963
0
{
964
0
    FcPattern *best, *ret = NULL;
965
966
0
    assert (sets != NULL);
967
0
    assert (p != NULL);
968
0
    assert (result != NULL);
969
970
0
    *result = FcResultNoMatch;
971
972
0
    config = FcConfigReference (config);
973
0
    if (!config)
974
0
  return NULL;
975
0
    best = FcFontSetMatchInternal (sets, nsets, p, result);
976
0
    if (best) {
977
0
  ret = FcFontRenderPrepare (config, p, best);
978
0
  FcPatternDestroy (best);
979
0
    }
980
981
0
    FcConfigDestroy (config);
982
983
0
    return ret;
984
0
}
985
986
FcPattern *
987
FcFontMatch (FcConfig  *config,
988
             FcPattern *p,
989
             FcResult  *result)
990
0
{
991
0
    FcFontSet *sets[2];
992
0
    int        nsets;
993
0
    FcPattern *best, *ret = NULL;
994
995
0
    assert (p != NULL);
996
0
    assert (result != NULL);
997
998
0
    *result = FcResultNoMatch;
999
1000
0
    config = FcConfigReference (config);
1001
0
    if (!config)
1002
0
  return NULL;
1003
0
    nsets = 0;
1004
0
    if (config->fonts[FcSetSystem])
1005
0
  sets[nsets++] = config->fonts[FcSetSystem];
1006
0
    if (config->fonts[FcSetApplication])
1007
0
  sets[nsets++] = config->fonts[FcSetApplication];
1008
1009
0
    best = FcFontSetMatchInternal (sets, nsets, p, result);
1010
0
    if (best) {
1011
0
  ret = FcFontRenderPrepare (config, p, best);
1012
0
  FcPatternDestroy (best);
1013
0
    }
1014
1015
0
    FcConfigDestroy (config);
1016
1017
0
    return ret;
1018
0
}
1019
1020
typedef struct _FcSortNode {
1021
    FcPattern *pattern;
1022
    double     score[PRI_END];
1023
} FcSortNode;
1024
1025
static int
1026
FcSortCompare (const void *aa, const void *ab)
1027
23.4M
{
1028
23.4M
    FcSortNode *a = *(FcSortNode **)aa;
1029
23.4M
    FcSortNode *b = *(FcSortNode **)ab;
1030
23.4M
    double     *as = &a->score[0];
1031
23.4M
    double     *bs = &b->score[0];
1032
23.4M
    double      ad = 0, bd = 0;
1033
23.4M
    int         i;
1034
1035
23.4M
    i = PRI_END;
1036
541M
    while (i-- && (ad = *as++) == (bd = *bs++))
1037
518M
  ;
1038
23.4M
    return ad < bd ? -1 : ad > bd ? 1
1039
11.7M
                                  : 0;
1040
23.4M
}
1041
1042
static FcBool
1043
FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **csp, FcBool trim)
1044
198k
{
1045
198k
    FcBool     ret = FcFalse;
1046
198k
    FcCharSet *cs;
1047
198k
    int        i;
1048
1049
198k
    cs = 0;
1050
198k
    if (trim || csp) {
1051
0
  cs = FcCharSetCreate();
1052
0
  if (cs == NULL)
1053
0
      goto bail;
1054
0
    }
1055
1056
4.56M
    for (i = 0; i < nnode; i++) {
1057
4.36M
  FcSortNode *node = *n++;
1058
4.36M
  FcBool      adds_chars = FcFalse;
1059
1060
  /*
1061
   * Only fetch node charset if we'd need it
1062
   */
1063
4.36M
  if (cs) {
1064
0
      FcCharSet *ncs;
1065
1066
0
      if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) !=
1067
0
          FcResultMatch)
1068
0
    continue;
1069
1070
0
      if (!FcCharSetMerge (cs, ncs, &adds_chars))
1071
0
    goto bail;
1072
0
  }
1073
1074
  /*
1075
   * If this font isn't a subset of the previous fonts,
1076
   * add it to the list
1077
   */
1078
4.36M
  if (!i || !trim || adds_chars) {
1079
4.36M
      FcPatternReference (node->pattern);
1080
4.36M
      if (FcDebug() & FC_DBG_MATCHV) {
1081
0
    printf ("Add ");
1082
0
    FcPatternPrint (node->pattern);
1083
0
      }
1084
4.36M
      if (!FcFontSetAdd (fs, node->pattern)) {
1085
0
    FcPatternDestroy (node->pattern);
1086
0
    goto bail;
1087
0
      }
1088
4.36M
  }
1089
4.36M
    }
1090
198k
    if (csp) {
1091
0
  *csp = cs;
1092
0
  cs = 0;
1093
0
    }
1094
1095
198k
    ret = FcTrue;
1096
1097
198k
bail:
1098
198k
    if (cs)
1099
0
  FcCharSetDestroy (cs);
1100
1101
198k
    return ret;
1102
198k
}
1103
1104
void
1105
FcFontSetSortDestroy (FcFontSet *fs)
1106
0
{
1107
0
    FcFontSetDestroy (fs);
1108
0
}
1109
1110
FcFontSet *
1111
FcFontSetSort (FcConfig   *config,
1112
               FcFontSet **sets,
1113
               int         nsets,
1114
               FcPattern  *p,
1115
               FcBool      trim,
1116
               FcCharSet **csp,
1117
               FcResult   *result)
1118
198k
{
1119
198k
    FcFontSet    *ret;
1120
198k
    FcFontSet    *s;
1121
198k
    FcSortNode   *nodes;
1122
198k
    FcSortNode  **nodeps, **nodep;
1123
198k
    int           nnodes;
1124
198k
    FcSortNode   *newp;
1125
198k
    int           set;
1126
198k
    int           f;
1127
198k
    int           i;
1128
198k
    int           nPatternLang;
1129
198k
    FcBool       *patternLangSat;
1130
198k
    FcValue       patternLang;
1131
198k
    FcCompareData data;
1132
1133
198k
    assert (sets != NULL);
1134
198k
    assert (p != NULL);
1135
198k
    assert (result != NULL);
1136
1137
    /* There are some implementation that relying on the result of
1138
     * "result" to check if the return value of FcFontSetSort
1139
     * is valid or not.
1140
     * So we should initialize it to the conservative way since
1141
     * this function doesn't return NULL anymore.
1142
     */
1143
198k
    if (result)
1144
198k
  *result = FcResultNoMatch;
1145
1146
198k
    if (FcDebug() & FC_DBG_MATCH) {
1147
0
  printf ("Sort ");
1148
0
  FcPatternPrint (p);
1149
0
    }
1150
198k
    nnodes = 0;
1151
397k
    for (set = 0; set < nsets; set++) {
1152
198k
  s = sets[set];
1153
198k
  if (!s)
1154
0
      continue;
1155
198k
  nnodes += s->nfont;
1156
198k
    }
1157
198k
    if (!nnodes)
1158
0
  return FcFontSetCreate();
1159
1160
198k
    for (nPatternLang = 0;
1161
592k
         FcPatternGet (p, FC_LANG, nPatternLang, &patternLang) == FcResultMatch;
1162
394k
         nPatternLang++)
1163
394k
  ;
1164
1165
    /* freed below */
1166
198k
    nodes = malloc (nnodes * sizeof (FcSortNode) +
1167
198k
                    nnodes * sizeof (FcSortNode *) +
1168
198k
                    nPatternLang * sizeof (FcBool));
1169
198k
    if (!nodes)
1170
0
  goto bail0;
1171
198k
    nodeps = (FcSortNode **)(nodes + nnodes);
1172
198k
    patternLangSat = (FcBool *)(nodeps + nnodes);
1173
1174
198k
    FcCompareDataInit (p, &data);
1175
1176
198k
    newp = nodes;
1177
198k
    nodep = nodeps;
1178
397k
    for (set = 0; set < nsets; set++) {
1179
198k
  s = sets[set];
1180
198k
  if (!s)
1181
0
      continue;
1182
4.56M
  for (f = 0; f < s->nfont; f++) {
1183
4.36M
      if (FcDebug() & FC_DBG_MATCHV) {
1184
0
    printf ("Font %d ", f);
1185
0
    FcPatternPrint (s->fonts[f]);
1186
0
      }
1187
4.36M
      newp->pattern = s->fonts[f];
1188
4.36M
      if (!FcCompare (p, newp->pattern, newp->score, result, &data))
1189
0
    goto bail1;
1190
      /* TODO: Should we check a FcPattern in FcFontSet?
1191
       * This way may not work if someone has own list of application fonts
1192
       * That said, just to reduce the cost for lookup so far.
1193
             */
1194
4.36M
      if (config->prefer_app_fonts && s != config->fonts[FcSetApplication]) {
1195
0
    newp->score[PRI_ORDER] += 1000;
1196
0
      }
1197
4.36M
      if (FcDebug() & FC_DBG_MATCHV) {
1198
0
    printf ("Score");
1199
0
    for (i = 0; i < PRI_END; i++) {
1200
0
        printf (" %g", newp->score[i]);
1201
0
    }
1202
0
    printf ("\n");
1203
0
      }
1204
4.36M
      *nodep = newp;
1205
4.36M
      newp++;
1206
4.36M
      nodep++;
1207
4.36M
  }
1208
198k
    }
1209
1210
198k
    FcCompareDataClear (&data);
1211
1212
198k
    nnodes = newp - nodes;
1213
1214
198k
    qsort (nodeps, nnodes, sizeof (FcSortNode *),
1215
198k
           FcSortCompare);
1216
1217
592k
    for (i = 0; i < nPatternLang; i++)
1218
394k
  patternLangSat[i] = FcFalse;
1219
1220
4.56M
    for (f = 0; f < nnodes; f++) {
1221
4.36M
  FcBool satisfies = FcFalse;
1222
  /*
1223
   * If this node matches any language, go check
1224
   * which ones and satisfy those entries
1225
   */
1226
4.36M
  if (nodeps[f]->score[PRI_LANG] < 2000) {
1227
12.8M
      for (i = 0; i < nPatternLang; i++) {
1228
8.67M
    FcValue nodeLang;
1229
1230
8.67M
    if (!patternLangSat[i] &&
1231
4.50M
        FcPatternGet (p, FC_LANG, i, &patternLang) == FcResultMatch &&
1232
4.50M
        FcPatternGet (nodeps[f]->pattern, FC_LANG, 0, &nodeLang) == FcResultMatch) {
1233
4.50M
        FcValue matchValue;
1234
4.50M
        double  compare = FcCompareLang (&patternLang, &nodeLang, &matchValue);
1235
4.50M
        if (compare >= 0 && compare < 2) {
1236
198k
      if (FcDebug() & FC_DBG_MATCHV) {
1237
0
          FcChar8 *family;
1238
0
          FcChar8 *style;
1239
1240
0
          if (FcPatternGetString (nodeps[f]->pattern, FC_FAMILY, 0, &family) == FcResultMatch &&
1241
0
              FcPatternGetString (nodeps[f]->pattern, FC_STYLE, 0, &style) == FcResultMatch)
1242
0
        printf ("Font %s:%s matches language %d\n", family, style, i);
1243
0
      }
1244
198k
      patternLangSat[i] = FcTrue;
1245
198k
      satisfies = FcTrue;
1246
198k
      break;
1247
198k
        }
1248
4.50M
    }
1249
8.67M
      }
1250
4.36M
  }
1251
4.36M
  if (!satisfies) {
1252
4.17M
      nodeps[f]->score[PRI_LANG] = 10000.0;
1253
4.17M
  }
1254
4.36M
    }
1255
1256
    /*
1257
     * Re-sort once the language issues have been settled
1258
     */
1259
198k
    qsort (nodeps, nnodes, sizeof (FcSortNode *),
1260
198k
           FcSortCompare);
1261
1262
198k
    ret = FcFontSetCreate();
1263
198k
    if (!ret)
1264
0
  goto bail1;
1265
1266
198k
    if (!FcSortWalk (nodeps, nnodes, ret, csp, trim))
1267
0
  goto bail2;
1268
1269
198k
    free (nodes);
1270
1271
198k
    if (ret->nfont > 0) {
1272
198k
  *result = FcResultMatch;
1273
198k
  if (FcDebug() & FC_DBG_MATCH) {
1274
0
      printf ("First font ");
1275
0
      FcPatternPrint (ret->fonts[0]);
1276
0
  }
1277
198k
    }
1278
1279
198k
    return ret;
1280
1281
0
bail2:
1282
0
    FcFontSetDestroy (ret);
1283
0
bail1:
1284
0
    free (nodes);
1285
0
bail0:
1286
0
    return 0;
1287
0
}
1288
1289
FcFontSet *
1290
FcFontSort (FcConfig   *config,
1291
            FcPattern  *p,
1292
            FcBool      trim,
1293
            FcCharSet **csp,
1294
            FcResult   *result)
1295
198k
{
1296
198k
    FcFontSet *sets[2], *ret;
1297
198k
    int        nsets;
1298
1299
198k
    assert (p != NULL);
1300
198k
    assert (result != NULL);
1301
1302
198k
    *result = FcResultNoMatch;
1303
1304
198k
    config = FcConfigReference (config);
1305
198k
    if (!config)
1306
0
  return NULL;
1307
198k
    nsets = 0;
1308
198k
    if (config->fonts[FcSetSystem])
1309
198k
  sets[nsets++] = config->fonts[FcSetSystem];
1310
198k
    if (config->fonts[FcSetApplication])
1311
0
  sets[nsets++] = config->fonts[FcSetApplication];
1312
198k
    ret = FcFontSetSort (config, sets, nsets, p, trim, csp, result);
1313
198k
    FcConfigDestroy (config);
1314
1315
198k
    return ret;
1316
198k
}
1317
#define __fcmatch__
1318
#include "fcaliastail.h"
1319
#undef __fcmatch__