fclang.c revision ca08ab68
1/* 2 * fontconfig/src/fclang.c 3 * 4 * Copyright © 2002 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 <string.h> 26#include "fcint.h" 27#include "fcftint.h" 28 29typedef struct { 30 const FcChar8 lang[8]; 31 const FcCharSet charset; 32} FcLangCharSet; 33 34typedef struct { 35 int begin; 36 int end; 37} FcLangCharSetRange; 38 39#include "../fc-lang/fclang.h" 40 41struct _FcLangSet { 42 FcStrSet *extra; 43 FcChar32 map_size; 44 FcChar32 map[NUM_LANG_SET_MAP]; 45}; 46 47static int FcLangSetIndex (const FcChar8 *lang); 48 49 50static void 51FcLangSetBitSet (FcLangSet *ls, 52 unsigned int id) 53{ 54 int bucket; 55 56 id = fcLangCharSetIndices[id]; 57 bucket = id >> 5; 58 if (bucket >= ls->map_size) 59 return; /* shouldn't happen really */ 60 61 ls->map[bucket] |= ((FcChar32) 1 << (id & 0x1f)); 62} 63 64static FcBool 65FcLangSetBitGet (const FcLangSet *ls, 66 unsigned int id) 67{ 68 int bucket; 69 70 id = fcLangCharSetIndices[id]; 71 bucket = id >> 5; 72 if (bucket >= ls->map_size) 73 return FcFalse; 74 75 return ((ls->map[bucket] >> (id & 0x1f)) & 1) ? FcTrue : FcFalse; 76} 77 78static void 79FcLangSetBitReset (FcLangSet *ls, 80 unsigned int id) 81{ 82 int bucket; 83 84 id = fcLangCharSetIndices[id]; 85 bucket = id >> 5; 86 if (bucket >= ls->map_size) 87 return; /* shouldn't happen really */ 88 89 ls->map[bucket] &= ~((FcChar32) 1 << (id & 0x1f)); 90} 91 92FcLangSet * 93FcFreeTypeLangSet (const FcCharSet *charset, 94 const FcChar8 *exclusiveLang) 95{ 96 int i, j; 97 FcChar32 missing; 98 const FcCharSet *exclusiveCharset = 0; 99 FcLangSet *ls; 100 101 if (exclusiveLang) 102 exclusiveCharset = FcLangGetCharSet (exclusiveLang); 103 ls = FcLangSetCreate (); 104 if (!ls) 105 return 0; 106 if (FcDebug() & FC_DBG_LANGSET) 107 { 108 printf ("font charset"); 109 FcCharSetPrint (charset); 110 printf ("\n"); 111 } 112 for (i = 0; i < NUM_LANG_CHAR_SET; i++) 113 { 114 if (FcDebug() & FC_DBG_LANGSET) 115 { 116 printf ("%s charset", fcLangCharSets[i].lang); 117 FcCharSetPrint (&fcLangCharSets[i].charset); 118 printf ("\n"); 119 } 120 121 /* 122 * Check for Han charsets to make fonts 123 * which advertise support for a single language 124 * not support other Han languages 125 */ 126 if (exclusiveCharset && 127 FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang)) 128 { 129 if (fcLangCharSets[i].charset.num != exclusiveCharset->num) 130 continue; 131 132 for (j = 0; j < fcLangCharSets[i].charset.num; j++) 133 if (FcCharSetLeaf(&fcLangCharSets[i].charset, j) != 134 FcCharSetLeaf(exclusiveCharset, j)) 135 continue; 136 } 137 missing = FcCharSetSubtractCount (&fcLangCharSets[i].charset, charset); 138 if (FcDebug() & FC_DBG_SCANV) 139 { 140 if (missing && missing < 10) 141 { 142 FcCharSet *missed = FcCharSetSubtract (&fcLangCharSets[i].charset, 143 charset); 144 FcChar32 ucs4; 145 FcChar32 map[FC_CHARSET_MAP_SIZE]; 146 FcChar32 next; 147 148 printf ("\n%s(%u) ", fcLangCharSets[i].lang, missing); 149 printf ("{"); 150 for (ucs4 = FcCharSetFirstPage (missed, map, &next); 151 ucs4 != FC_CHARSET_DONE; 152 ucs4 = FcCharSetNextPage (missed, map, &next)) 153 { 154 int i, j; 155 for (i = 0; i < FC_CHARSET_MAP_SIZE; i++) 156 if (map[i]) 157 { 158 for (j = 0; j < 32; j++) 159 if (map[i] & (1 << j)) 160 printf (" %04x", ucs4 + i * 32 + j); 161 } 162 } 163 printf (" }\n\t"); 164 FcCharSetDestroy (missed); 165 } 166 else 167 printf ("%s(%u) ", fcLangCharSets[i].lang, missing); 168 } 169 if (!missing) 170 FcLangSetBitSet (ls, i); 171 } 172 173 if (FcDebug() & FC_DBG_SCANV) 174 printf ("\n"); 175 176 177 return ls; 178} 179 180FcChar8 * 181FcLangNormalize (const FcChar8 *lang) 182{ 183 FcChar8 *result = NULL, *s, *orig; 184 char *territory, *encoding, *modifier; 185 size_t llen, tlen = 0, mlen = 0; 186 187 if (!lang || !*lang) 188 return NULL; 189 190 if (FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C") == 0 || 191 FcStrCmpIgnoreCase (lang, (const FcChar8 *)"POSIX") == 0) 192 { 193 result = FcStrCopy ((const FcChar8 *)"en"); 194 goto bail; 195 } 196 197 s = FcStrCopy (lang); 198 if (!s) 199 goto bail; 200 201 /* from the comments in glibc: 202 * 203 * LOCALE can consist of up to four recognized parts for the XPG syntax: 204 * 205 * language[_territory[.codeset]][@modifier] 206 * 207 * Beside the first all of them are allowed to be missing. If the 208 * full specified locale is not found, the less specific one are 209 * looked for. The various part will be stripped off according to 210 * the following order: 211 * (1) codeset 212 * (2) normalized codeset 213 * (3) territory 214 * (4) modifier 215 * 216 * So since we don't take care of the codeset part here, what patterns 217 * we need to deal with is: 218 * 219 * 1. language_territory@modifier 220 * 2. language@modifier 221 * 3. language 222 * 223 * then. and maybe no need to try language_territory here. 224 */ 225 modifier = strchr ((const char *) s, '@'); 226 if (modifier) 227 { 228 *modifier = 0; 229 modifier++; 230 mlen = strlen (modifier); 231 } 232 encoding = strchr ((const char *) s, '.'); 233 if (encoding) 234 { 235 *encoding = 0; 236 encoding++; 237 if (modifier) 238 { 239 memmove (encoding, modifier, mlen + 1); 240 modifier = encoding; 241 } 242 } 243 territory = strchr ((const char *) s, '_'); 244 if (!territory) 245 territory = strchr ((const char *) s, '-'); 246 if (territory) 247 { 248 *territory = 0; 249 territory++; 250 tlen = strlen (territory); 251 } 252 llen = strlen ((const char *) s); 253 if (llen < 2 || llen > 3) 254 { 255 fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid language tag\n", 256 lang); 257 goto bail0; 258 } 259 if (territory && (tlen < 2 || tlen > 3)) 260 { 261 fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid region tag\n", 262 lang); 263 goto bail0; 264 } 265 if (territory) 266 territory[-1] = '-'; 267 if (modifier) 268 modifier[-1] = '@'; 269 orig = FcStrDowncase (s); 270 if (!orig) 271 goto bail0; 272 if (territory) 273 { 274 if (FcDebug () & FC_DBG_LANGSET) 275 printf("Checking the existence of %s.orth\n", s); 276 if (FcLangSetIndex (s) < 0) 277 { 278 memmove (territory - 1, territory + tlen, (mlen > 0 ? mlen + 1 : 0) + 1); 279 if (modifier) 280 modifier = territory; 281 } 282 else 283 { 284 result = s; 285 s = NULL; 286 goto bail1; 287 } 288 } 289 if (modifier) 290 { 291 if (FcDebug () & FC_DBG_LANGSET) 292 printf("Checking the existence of %s.orth\n", s); 293 if (FcLangSetIndex (s) < 0) 294 modifier[-1] = 0; 295 else 296 { 297 result = s; 298 s = NULL; 299 goto bail1; 300 } 301 } 302 if (FcDebug () & FC_DBG_LANGSET) 303 printf("Checking the existence of %s.orth\n", s); 304 if (FcLangSetIndex (s) < 0) 305 { 306 /* there seems no languages matched in orth. 307 * add the language as is for fallback. 308 */ 309 result = orig; 310 orig = NULL; 311 } 312 else 313 { 314 result = s; 315 s = NULL; 316 } 317 bail1: 318 if (orig) 319 free (orig); 320 bail0: 321 if (s) 322 free (s); 323 bail: 324 if (FcDebug () & FC_DBG_LANGSET) 325 { 326 if (result) 327 printf ("normalized: %s -> %s\n", lang, result); 328 else 329 printf ("Unable to normalize %s\n", lang); 330 } 331 332 return result; 333} 334 335#define FcLangEnd(c) ((c) == '-' || (c) == '\0') 336 337FcLangResult 338FcLangCompare (const FcChar8 *s1, const FcChar8 *s2) 339{ 340 FcChar8 c1, c2; 341 FcLangResult result = FcLangDifferentLang; 342 343 for (;;) 344 { 345 c1 = *s1++; 346 c2 = *s2++; 347 348 c1 = FcToLower (c1); 349 c2 = FcToLower (c2); 350 if (c1 != c2) 351 { 352 if (FcLangEnd (c1) && FcLangEnd (c2)) 353 result = FcLangDifferentTerritory; 354 return result; 355 } 356 else if (!c1) 357 return FcLangEqual; 358 else if (c1 == '-') 359 result = FcLangDifferentTerritory; 360 } 361} 362 363/* 364 * Return FcTrue when super contains sub. 365 * 366 * super contains sub if super and sub have the same 367 * language and either the same country or one 368 * is missing the country 369 */ 370 371static FcBool 372FcLangContains (const FcChar8 *super, const FcChar8 *sub) 373{ 374 FcChar8 c1, c2; 375 376 for (;;) 377 { 378 c1 = *super++; 379 c2 = *sub++; 380 381 c1 = FcToLower (c1); 382 c2 = FcToLower (c2); 383 if (c1 != c2) 384 { 385 /* see if super has a country while sub is mising one */ 386 if (c1 == '-' && c2 == '\0') 387 return FcTrue; 388 /* see if sub has a country while super is mising one */ 389 if (c1 == '\0' && c2 == '-') 390 return FcTrue; 391 return FcFalse; 392 } 393 else if (!c1) 394 return FcTrue; 395 } 396} 397 398const FcCharSet * 399FcLangGetCharSet (const FcChar8 *lang) 400{ 401 int i; 402 int country = -1; 403 404 for (i = 0; i < NUM_LANG_CHAR_SET; i++) 405 { 406 switch (FcLangCompare (lang, fcLangCharSets[i].lang)) { 407 case FcLangEqual: 408 return &fcLangCharSets[i].charset; 409 case FcLangDifferentTerritory: 410 if (country == -1) 411 country = i; 412 case FcLangDifferentLang: 413 default: 414 break; 415 } 416 } 417 if (country == -1) 418 return 0; 419 return &fcLangCharSets[country].charset; 420} 421 422FcStrSet * 423FcGetLangs (void) 424{ 425 FcStrSet *langs; 426 int i; 427 428 langs = FcStrSetCreate(); 429 if (!langs) 430 return 0; 431 432 for (i = 0; i < NUM_LANG_CHAR_SET; i++) 433 FcStrSetAdd (langs, fcLangCharSets[i].lang); 434 435 return langs; 436} 437 438FcLangSet * 439FcLangSetCreate (void) 440{ 441 FcLangSet *ls; 442 443 ls = malloc (sizeof (FcLangSet)); 444 if (!ls) 445 return 0; 446 FcMemAlloc (FC_MEM_LANGSET, sizeof (FcLangSet)); 447 memset (ls->map, '\0', sizeof (ls->map)); 448 ls->map_size = NUM_LANG_SET_MAP; 449 ls->extra = 0; 450 return ls; 451} 452 453void 454FcLangSetDestroy (FcLangSet *ls) 455{ 456 if (ls->extra) 457 FcStrSetDestroy (ls->extra); 458 FcMemFree (FC_MEM_LANGSET, sizeof (FcLangSet)); 459 free (ls); 460} 461 462FcLangSet * 463FcLangSetCopy (const FcLangSet *ls) 464{ 465 FcLangSet *new; 466 467 new = FcLangSetCreate (); 468 if (!new) 469 goto bail0; 470 memset (new->map, '\0', sizeof (new->map)); 471 memcpy (new->map, ls->map, FC_MIN (sizeof (new->map), ls->map_size * sizeof (ls->map[0]))); 472 if (ls->extra) 473 { 474 FcStrList *list; 475 FcChar8 *extra; 476 477 new->extra = FcStrSetCreate (); 478 if (!new->extra) 479 goto bail1; 480 481 list = FcStrListCreate (ls->extra); 482 if (!list) 483 goto bail1; 484 485 while ((extra = FcStrListNext (list))) 486 if (!FcStrSetAdd (new->extra, extra)) 487 { 488 FcStrListDone (list); 489 goto bail1; 490 } 491 FcStrListDone (list); 492 } 493 return new; 494bail1: 495 FcLangSetDestroy (new); 496bail0: 497 return 0; 498} 499 500static int 501FcLangSetIndex (const FcChar8 *lang) 502{ 503 int low, high, mid = 0; 504 int cmp = 0; 505 FcChar8 firstChar = FcToLower(lang[0]); 506 FcChar8 secondChar = firstChar ? FcToLower(lang[1]) : '\0'; 507 508 if (firstChar < 'a') 509 { 510 low = 0; 511 high = fcLangCharSetRanges[0].begin; 512 } 513 else if(firstChar > 'z') 514 { 515 low = fcLangCharSetRanges[25].begin; 516 high = NUM_LANG_CHAR_SET - 1; 517 } 518 else 519 { 520 low = fcLangCharSetRanges[firstChar - 'a'].begin; 521 high = fcLangCharSetRanges[firstChar - 'a'].end; 522 /* no matches */ 523 if (low > high) 524 return -low; /* next entry after where it would be */ 525 } 526 527 while (low <= high) 528 { 529 mid = (high + low) >> 1; 530 if(fcLangCharSets[mid].lang[0] != firstChar) 531 cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang, lang); 532 else 533 { /* fast path for resolving 2-letter languages (by far the most common) after 534 * finding the first char (probably already true because of the hash table) */ 535 cmp = fcLangCharSets[mid].lang[1] - secondChar; 536 if (cmp == 0 && 537 (fcLangCharSets[mid].lang[2] != '\0' || 538 lang[2] != '\0')) 539 { 540 cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang+2, 541 lang+2); 542 } 543 } 544 if (cmp == 0) 545 return mid; 546 if (cmp < 0) 547 low = mid + 1; 548 else 549 high = mid - 1; 550 } 551 if (cmp < 0) 552 mid++; 553 return -(mid + 1); 554} 555 556FcBool 557FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang) 558{ 559 int id; 560 561 id = FcLangSetIndex (lang); 562 if (id >= 0) 563 { 564 FcLangSetBitSet (ls, id); 565 return FcTrue; 566 } 567 if (!ls->extra) 568 { 569 ls->extra = FcStrSetCreate (); 570 if (!ls->extra) 571 return FcFalse; 572 } 573 return FcStrSetAdd (ls->extra, lang); 574} 575 576FcBool 577FcLangSetDel (FcLangSet *ls, const FcChar8 *lang) 578{ 579 int id; 580 581 id = FcLangSetIndex (lang); 582 if (id >= 0) 583 { 584 FcLangSetBitReset (ls, id); 585 } 586 else if (ls->extra) 587 { 588 FcStrSetDel (ls->extra, lang); 589 } 590 return FcTrue; 591} 592 593FcLangResult 594FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang) 595{ 596 int id; 597 FcLangResult best, r; 598 int i; 599 600 id = FcLangSetIndex (lang); 601 if (id < 0) 602 id = -id - 1; 603 else if (FcLangSetBitGet (ls, id)) 604 return FcLangEqual; 605 best = FcLangDifferentLang; 606 for (i = id - 1; i >= 0; i--) 607 { 608 r = FcLangCompare (lang, fcLangCharSets[i].lang); 609 if (r == FcLangDifferentLang) 610 break; 611 if (FcLangSetBitGet (ls, i) && r < best) 612 best = r; 613 } 614 for (i = id; i < NUM_LANG_CHAR_SET; i++) 615 { 616 r = FcLangCompare (lang, fcLangCharSets[i].lang); 617 if (r == FcLangDifferentLang) 618 break; 619 if (FcLangSetBitGet (ls, i) && r < best) 620 best = r; 621 } 622 if (ls->extra) 623 { 624 FcStrList *list = FcStrListCreate (ls->extra); 625 FcChar8 *extra; 626 627 if (list) 628 { 629 while (best > FcLangEqual && (extra = FcStrListNext (list))) 630 { 631 r = FcLangCompare (lang, extra); 632 if (r < best) 633 best = r; 634 } 635 FcStrListDone (list); 636 } 637 } 638 return best; 639} 640 641static FcLangResult 642FcLangSetCompareStrSet (const FcLangSet *ls, FcStrSet *set) 643{ 644 FcStrList *list = FcStrListCreate (set); 645 FcLangResult r, best = FcLangDifferentLang; 646 FcChar8 *extra; 647 648 if (list) 649 { 650 while (best > FcLangEqual && (extra = FcStrListNext (list))) 651 { 652 r = FcLangSetHasLang (ls, extra); 653 if (r < best) 654 best = r; 655 } 656 FcStrListDone (list); 657 } 658 return best; 659} 660 661FcLangResult 662FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb) 663{ 664 int i, j, count; 665 FcLangResult best, r; 666 667 count = FC_MIN (lsa->map_size, lsb->map_size); 668 count = FC_MIN (NUM_LANG_SET_MAP, count); 669 for (i = 0; i < count; i++) 670 if (lsa->map[i] & lsb->map[i]) 671 return FcLangEqual; 672 best = FcLangDifferentLang; 673 for (j = 0; j < NUM_COUNTRY_SET; j++) 674 for (i = 0; i < count; i++) 675 if ((lsa->map[i] & fcLangCountrySets[j][i]) && 676 (lsb->map[i] & fcLangCountrySets[j][i])) 677 { 678 best = FcLangDifferentTerritory; 679 break; 680 } 681 if (lsa->extra) 682 { 683 r = FcLangSetCompareStrSet (lsb, lsa->extra); 684 if (r < best) 685 best = r; 686 } 687 if (best > FcLangEqual && lsb->extra) 688 { 689 r = FcLangSetCompareStrSet (lsa, lsb->extra); 690 if (r < best) 691 best = r; 692 } 693 return best; 694} 695 696/* 697 * Used in computing values -- mustn't allocate any storage 698 * XXX Not thread-safe 699 */ 700FcLangSet * 701FcLangSetPromote (const FcChar8 *lang) 702{ 703 static FcLangSet ls; 704 static FcStrSet strs; 705 static FcChar8 *str; 706 int id; 707 708 memset (ls.map, '\0', sizeof (ls.map)); 709 ls.map_size = NUM_LANG_SET_MAP; 710 ls.extra = 0; 711 id = FcLangSetIndex (lang); 712 if (id > 0) 713 { 714 FcLangSetBitSet (&ls, id); 715 } 716 else 717 { 718 ls.extra = &strs; 719 strs.num = 1; 720 strs.size = 1; 721 strs.strs = &str; 722 strs.ref = 1; 723 str = (FcChar8 *) lang; 724 } 725 return &ls; 726} 727 728FcChar32 729FcLangSetHash (const FcLangSet *ls) 730{ 731 FcChar32 h = 0; 732 int i, count; 733 734 count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP); 735 for (i = 0; i < count; i++) 736 h ^= ls->map[i]; 737 if (ls->extra) 738 h ^= ls->extra->num; 739 return h; 740} 741 742FcLangSet * 743FcNameParseLangSet (const FcChar8 *string) 744{ 745 FcChar8 lang[32], c = 0; 746 int i; 747 FcLangSet *ls; 748 749 ls = FcLangSetCreate (); 750 if (!ls) 751 goto bail0; 752 753 for(;;) 754 { 755 for(i = 0; i < 31;i++) 756 { 757 c = *string++; 758 if(c == '\0' || c == '|') 759 break; /* end of this code */ 760 lang[i] = c; 761 } 762 lang[i] = '\0'; 763 if (!FcLangSetAdd (ls, lang)) 764 goto bail1; 765 if(c == '\0') 766 break; 767 } 768 return ls; 769bail1: 770 FcLangSetDestroy (ls); 771bail0: 772 return 0; 773} 774 775FcBool 776FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls) 777{ 778 int i, bit, count; 779 FcChar32 bits; 780 FcBool first = FcTrue; 781 782 count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP); 783 for (i = 0; i < count; i++) 784 { 785 if ((bits = ls->map[i])) 786 { 787 for (bit = 0; bit <= 31; bit++) 788 if (bits & (1 << bit)) 789 { 790 int id = (i << 5) | bit; 791 if (!first) 792 if (!FcStrBufChar (buf, '|')) 793 return FcFalse; 794 if (!FcStrBufString (buf, fcLangCharSets[fcLangCharSetIndicesInv[id]].lang)) 795 return FcFalse; 796 first = FcFalse; 797 } 798 } 799 } 800 if (ls->extra) 801 { 802 FcStrList *list = FcStrListCreate (ls->extra); 803 FcChar8 *extra; 804 805 if (!list) 806 return FcFalse; 807 while ((extra = FcStrListNext (list))) 808 { 809 if (!first) 810 if (!FcStrBufChar (buf, '|')) 811 { 812 FcStrListDone (list); 813 return FcFalse; 814 } 815 if (!FcStrBufString (buf, extra)) 816 { 817 FcStrListDone (list); 818 return FcFalse; 819 } 820 first = FcFalse; 821 } 822 FcStrListDone (list); 823 } 824 return FcTrue; 825} 826 827FcBool 828FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb) 829{ 830 int i, count; 831 832 count = FC_MIN (lsa->map_size, lsb->map_size); 833 count = FC_MIN (NUM_LANG_SET_MAP, count); 834 for (i = 0; i < count; i++) 835 { 836 if (lsa->map[i] != lsb->map[i]) 837 return FcFalse; 838 } 839 if (!lsa->extra && !lsb->extra) 840 return FcTrue; 841 if (lsa->extra && lsb->extra) 842 return FcStrSetEqual (lsa->extra, lsb->extra); 843 return FcFalse; 844} 845 846static FcBool 847FcLangSetContainsLang (const FcLangSet *ls, const FcChar8 *lang) 848{ 849 int id; 850 int i; 851 852 id = FcLangSetIndex (lang); 853 if (id < 0) 854 id = -id - 1; 855 else if (FcLangSetBitGet (ls, id)) 856 return FcTrue; 857 /* 858 * search up and down among equal languages for a match 859 */ 860 for (i = id - 1; i >= 0; i--) 861 { 862 if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang) 863 break; 864 if (FcLangSetBitGet (ls, i) && 865 FcLangContains (fcLangCharSets[i].lang, lang)) 866 return FcTrue; 867 } 868 for (i = id; i < NUM_LANG_CHAR_SET; i++) 869 { 870 if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang) 871 break; 872 if (FcLangSetBitGet (ls, i) && 873 FcLangContains (fcLangCharSets[i].lang, lang)) 874 return FcTrue; 875 } 876 if (ls->extra) 877 { 878 FcStrList *list = FcStrListCreate (ls->extra); 879 FcChar8 *extra; 880 881 if (list) 882 { 883 while ((extra = FcStrListNext (list))) 884 { 885 if (FcLangContains (extra, lang)) 886 break; 887 } 888 FcStrListDone (list); 889 if (extra) 890 return FcTrue; 891 } 892 } 893 return FcFalse; 894} 895 896/* 897 * return FcTrue if lsa contains every language in lsb 898 */ 899FcBool 900FcLangSetContains (const FcLangSet *lsa, const FcLangSet *lsb) 901{ 902 int i, j, count; 903 FcChar32 missing; 904 905 if (FcDebug() & FC_DBG_MATCHV) 906 { 907 printf ("FcLangSet "); FcLangSetPrint (lsa); 908 printf (" contains "); FcLangSetPrint (lsb); 909 printf ("\n"); 910 } 911 /* 912 * check bitmaps for missing language support 913 */ 914 count = FC_MIN (lsa->map_size, lsb->map_size); 915 count = FC_MIN (NUM_LANG_SET_MAP, count); 916 for (i = 0; i < count; i++) 917 { 918 missing = lsb->map[i] & ~lsa->map[i]; 919 if (missing) 920 { 921 for (j = 0; j < 32; j++) 922 if (missing & (1 << j)) 923 { 924 if (!FcLangSetContainsLang (lsa, 925 fcLangCharSets[fcLangCharSetIndicesInv[i*32 + j]].lang)) 926 { 927 if (FcDebug() & FC_DBG_MATCHV) 928 printf ("\tMissing bitmap %s\n", fcLangCharSets[fcLangCharSetIndicesInv[i*32+j]].lang); 929 return FcFalse; 930 } 931 } 932 } 933 } 934 if (lsb->extra) 935 { 936 FcStrList *list = FcStrListCreate (lsb->extra); 937 FcChar8 *extra; 938 939 if (list) 940 { 941 while ((extra = FcStrListNext (list))) 942 { 943 if (!FcLangSetContainsLang (lsa, extra)) 944 { 945 if (FcDebug() & FC_DBG_MATCHV) 946 printf ("\tMissing string %s\n", extra); 947 break; 948 } 949 } 950 FcStrListDone (list); 951 if (extra) 952 return FcFalse; 953 } 954 } 955 return FcTrue; 956} 957 958FcBool 959FcLangSetSerializeAlloc (FcSerialize *serialize, const FcLangSet *l) 960{ 961 if (!FcSerializeAlloc (serialize, l, sizeof (FcLangSet))) 962 return FcFalse; 963 return FcTrue; 964} 965 966FcLangSet * 967FcLangSetSerialize(FcSerialize *serialize, const FcLangSet *l) 968{ 969 FcLangSet *l_serialize = FcSerializePtr (serialize, l); 970 971 if (!l_serialize) 972 return NULL; 973 memset (l_serialize->map, '\0', sizeof (l_serialize->map)); 974 memcpy (l_serialize->map, l->map, FC_MIN (sizeof (l_serialize->map), l->map_size * sizeof (l->map[0]))); 975 l_serialize->map_size = NUM_LANG_SET_MAP; 976 l_serialize->extra = NULL; /* We don't serialize ls->extra */ 977 return l_serialize; 978} 979 980FcStrSet * 981FcLangSetGetLangs (const FcLangSet *ls) 982{ 983 FcStrSet *langs; 984 int i; 985 986 langs = FcStrSetCreate(); 987 if (!langs) 988 return 0; 989 990 for (i = 0; i < NUM_LANG_CHAR_SET; i++) 991 if (FcLangSetBitGet (ls, i)) 992 FcStrSetAdd (langs, fcLangCharSets[i].lang); 993 994 if (ls->extra) 995 { 996 FcStrList *list = FcStrListCreate (ls->extra); 997 FcChar8 *extra; 998 999 if (list) 1000 { 1001 while ((extra = FcStrListNext (list))) 1002 FcStrSetAdd (langs, extra); 1003 1004 FcStrListDone (list); 1005 } 1006 } 1007 1008 return langs; 1009} 1010 1011static FcLangSet * 1012FcLangSetOperate(const FcLangSet *a, 1013 const FcLangSet *b, 1014 FcBool (*func) (FcLangSet *ls, 1015 const FcChar8 *s)) 1016{ 1017 FcLangSet *langset = FcLangSetCopy (a); 1018 FcStrList *sl = FcStrListCreate (FcLangSetGetLangs (b)); 1019 FcChar8 *str; 1020 1021 while ((str = FcStrListNext (sl))) 1022 { 1023 func (langset, str); 1024 } 1025 FcStrListDone (sl); 1026 1027 return langset; 1028} 1029 1030FcLangSet * 1031FcLangSetUnion (const FcLangSet *a, const FcLangSet *b) 1032{ 1033 return FcLangSetOperate(a, b, FcLangSetAdd); 1034} 1035 1036FcLangSet * 1037FcLangSetSubtract (const FcLangSet *a, const FcLangSet *b) 1038{ 1039 return FcLangSetOperate(a, b, FcLangSetDel); 1040} 1041 1042#define __fclang__ 1043#include "fcaliastail.h" 1044#include "fcftaliastail.h" 1045#undef __fclang__ 1046