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