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