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