1 /* 2 * Copyright 2009,2010 Red Hat, Inc. 3 * Copyright 2011,2012 Google, Inc. 4 * 5 * This is part of HarfBuzz, a text shaping library. 6 * 7 * Permission is hereby granted, without written agreement and without 8 * license or royalty fees, to use, copy, modify, and distribute this 9 * software and its documentation for any purpose, provided that the 10 * above copyright notice and the following two paragraphs appear in 11 * all copies of this software. 12 * 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 17 * DAMAGE. 18 * 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 * 25 * Red Hat Author(s): Behdad Esfahbod 26 * Google Author(s): Behdad Esfahbod 27 */ 28 29 #include "hb-private.hh" 30 31 #include "hb-mutex-private.hh" 32 #include "hb-object-private.hh" 33 34 #include <locale.h> 35 #ifdef HAVE_XLOCALE_H 36 #include <xlocale.h> 37 #endif 38 39 40 /* hb_options_t */ 41 42 hb_options_union_t _hb_options; 43 44 void 45 _hb_options_init (void) 46 { 47 hb_options_union_t u; 48 u.i = 0; 49 u.opts.initialized = 1; 50 51 char *c = getenv ("HB_OPTIONS"); 52 u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible"); 53 54 /* This is idempotent and threadsafe. */ 55 _hb_options = u; 56 } 57 58 59 /* hb_tag_t */ 60 61 /** 62 * hb_tag_from_string: 63 * @str: (array length=len) (element-type uint8_t): 64 * @len: 65 * 66 * 67 * 68 * Return value: 69 * 70 * Since: 0.9.2 71 **/ 72 hb_tag_t 73 hb_tag_from_string (const char *str, int len) 74 { 75 char tag[4]; 76 unsigned int i; 77 78 if (!str || !len || !*str) 79 return HB_TAG_NONE; 80 81 if (len < 0 || len > 4) 82 len = 4; 83 for (i = 0; i < (unsigned) len && str[i]; i++) 84 tag[i] = str[i]; 85 for (; i < 4; i++) 86 tag[i] = ' '; 87 88 return HB_TAG (tag[0], tag[1], tag[2], tag[3]); 89 } 90 91 /** 92 * hb_tag_to_string: 93 * @tag: 94 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): 95 * 96 * 97 * 98 * Since: 0.9.5 99 **/ 100 void 101 hb_tag_to_string (hb_tag_t tag, char *buf) 102 { 103 buf[0] = (char) (uint8_t) (tag >> 24); 104 buf[1] = (char) (uint8_t) (tag >> 16); 105 buf[2] = (char) (uint8_t) (tag >> 8); 106 buf[3] = (char) (uint8_t) (tag >> 0); 107 } 108 109 110 /* hb_direction_t */ 111 112 const char direction_strings[][4] = { 113 "ltr", 114 "rtl", 115 "ttb", 116 "btt" 117 }; 118 119 /** 120 * hb_direction_from_string: 121 * @str: (array length=len) (element-type uint8_t): 122 * @len: 123 * 124 * 125 * 126 * Return value: 127 * 128 * Since: 0.9.2 129 **/ 130 hb_direction_t 131 hb_direction_from_string (const char *str, int len) 132 { 133 if (unlikely (!str || !len || !*str)) 134 return HB_DIRECTION_INVALID; 135 136 /* Lets match loosely: just match the first letter, such that 137 * all of "ltr", "left-to-right", etc work! 138 */ 139 char c = TOLOWER (str[0]); 140 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++) 141 if (c == direction_strings[i][0]) 142 return (hb_direction_t) (HB_DIRECTION_LTR + i); 143 144 return HB_DIRECTION_INVALID; 145 } 146 147 /** 148 * hb_direction_to_string: 149 * @direction: 150 * 151 * 152 * 153 * Return value: (transfer none): 154 * 155 * Since: 0.9.2 156 **/ 157 const char * 158 hb_direction_to_string (hb_direction_t direction) 159 { 160 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR) 161 < ARRAY_LENGTH (direction_strings))) 162 return direction_strings[direction - HB_DIRECTION_LTR]; 163 164 return "invalid"; 165 } 166 167 168 /* hb_language_t */ 169 170 struct hb_language_impl_t { 171 const char s[1]; 172 }; 173 174 static const char canon_map[256] = { 175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 178 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, 179 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 180 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', 181 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 182 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 183 }; 184 185 static bool 186 lang_equal (hb_language_t v1, 187 const void *v2) 188 { 189 const unsigned char *p1 = (const unsigned char *) v1; 190 const unsigned char *p2 = (const unsigned char *) v2; 191 192 while (*p1 && *p1 == canon_map[*p2]) { 193 p1++; 194 p2++; 195 } 196 197 return *p1 == canon_map[*p2]; 198 } 199 200 #if 0 201 static unsigned int 202 lang_hash (const void *key) 203 { 204 const unsigned char *p = key; 205 unsigned int h = 0; 206 while (canon_map[*p]) 207 { 208 h = (h << 5) - h + canon_map[*p]; 209 p++; 210 } 211 212 return h; 213 } 214 #endif 215 216 217 struct hb_language_item_t { 218 219 struct hb_language_item_t *next; 220 hb_language_t lang; 221 222 inline bool operator == (const char *s) const { 223 return lang_equal (lang, s); 224 } 225 226 inline hb_language_item_t & operator = (const char *s) { 227 /* If a custom allocated is used calling strdup() pairs 228 badly with a call to the custom free() in finish() below. 229 Therefore don't call strdup(), implement its behavior. 230 */ 231 size_t len = strlen(s) + 1; 232 lang = (hb_language_t) malloc(len); 233 if (likely (lang)) 234 { 235 memcpy((unsigned char *) lang, s, len); 236 for (unsigned char *p = (unsigned char *) lang; *p; p++) 237 *p = canon_map[*p]; 238 } 239 240 return *this; 241 } 242 243 void finish (void) { free ((void *) lang); } 244 }; 245 246 247 /* Thread-safe lock-free language list */ 248 249 static hb_language_item_t *langs; 250 251 #ifdef HB_USE_ATEXIT 252 static void 253 free_langs (void) 254 { 255 while (langs) { 256 hb_language_item_t *next = langs->next; 257 langs->finish (); 258 free (langs); 259 langs = next; 260 } 261 } 262 #endif 263 264 static hb_language_item_t * 265 lang_find_or_insert (const char *key) 266 { 267 retry: 268 hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs); 269 270 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next) 271 if (*lang == key) 272 return lang; 273 274 /* Not found; allocate one. */ 275 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); 276 if (unlikely (!lang)) 277 return nullptr; 278 lang->next = first_lang; 279 *lang = key; 280 if (unlikely (!lang->lang)) 281 { 282 free (lang); 283 return nullptr; 284 } 285 286 if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) { 287 lang->finish (); 288 free (lang); 289 goto retry; 290 } 291 292 #ifdef HB_USE_ATEXIT 293 if (!first_lang) 294 atexit (free_langs); /* First person registers atexit() callback. */ 295 #endif 296 297 return lang; 298 } 299 300 301 /** 302 * hb_language_from_string: 303 * @str: (array length=len) (element-type uint8_t): a string representing 304 * ISO639 language code 305 * @len: length of the @str, or -1 if it is %NULL-terminated. 306 * 307 * Converts @str representing an ISO639 language code to the corresponding 308 * #hb_language_t. 309 * 310 * Return value: (transfer none): 311 * The #hb_language_t corresponding to the ISO639 language code. 312 * 313 * Since: 0.9.2 314 **/ 315 hb_language_t 316 hb_language_from_string (const char *str, int len) 317 { 318 if (!str || !len || !*str) 319 return HB_LANGUAGE_INVALID; 320 321 hb_language_item_t *item = nullptr; 322 if (len >= 0) 323 { 324 /* NUL-terminate it. */ 325 char strbuf[64]; 326 len = MIN (len, (int) sizeof (strbuf) - 1); 327 memcpy (strbuf, str, len); 328 strbuf[len] = '\0'; 329 item = lang_find_or_insert (strbuf); 330 } 331 else 332 item = lang_find_or_insert (str); 333 334 return likely (item) ? item->lang : HB_LANGUAGE_INVALID; 335 } 336 337 /** 338 * hb_language_to_string: 339 * @language: an #hb_language_t to convert. 340 * 341 * See hb_language_from_string(). 342 * 343 * Return value: (transfer none): 344 * A %NULL-terminated string representing the @language. Must not be freed by 345 * the caller. 346 * 347 * Since: 0.9.2 348 **/ 349 const char * 350 hb_language_to_string (hb_language_t language) 351 { 352 /* This is actually nullptr-safe! */ 353 return language->s; 354 } 355 356 /** 357 * hb_language_get_default: 358 * 359 * 360 * 361 * Return value: (transfer none): 362 * 363 * Since: 0.9.2 364 **/ 365 hb_language_t 366 hb_language_get_default (void) 367 { 368 static hb_language_t default_language = HB_LANGUAGE_INVALID; 369 370 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language); 371 if (unlikely (language == HB_LANGUAGE_INVALID)) { 372 language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1); 373 (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language); 374 } 375 376 return default_language; 377 } 378 379 380 /* hb_script_t */ 381 382 /** 383 * hb_script_from_iso15924_tag: 384 * @tag: an #hb_tag_t representing an ISO15924 tag. 385 * 386 * Converts an ISO15924 script tag to a corresponding #hb_script_t. 387 * 388 * Return value: 389 * An #hb_script_t corresponding to the ISO15924 tag. 390 * 391 * Since: 0.9.2 392 **/ 393 hb_script_t 394 hb_script_from_iso15924_tag (hb_tag_t tag) 395 { 396 if (unlikely (tag == HB_TAG_NONE)) 397 return HB_SCRIPT_INVALID; 398 399 /* Be lenient, adjust case (one capital letter followed by three small letters) */ 400 tag = (tag & 0xDFDFDFDFu) | 0x00202020u; 401 402 switch (tag) { 403 404 /* These graduated from the 'Q' private-area codes, but 405 * the old code is still aliased by Unicode, and the Qaai 406 * one in use by ICU. */ 407 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; 408 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; 409 410 /* Script variants from http://unicode.org/iso15924/ */ 411 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; 412 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; 413 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; 414 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; 415 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC; 416 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC; 417 } 418 419 /* If it looks right, just use the tag as a script */ 420 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u) 421 return (hb_script_t) tag; 422 423 /* Otherwise, return unknown */ 424 return HB_SCRIPT_UNKNOWN; 425 } 426 427 /** 428 * hb_script_from_string: 429 * @str: (array length=len) (element-type uint8_t): a string representing an 430 * ISO15924 tag. 431 * @len: length of the @str, or -1 if it is %NULL-terminated. 432 * 433 * Converts a string @str representing an ISO15924 script tag to a 434 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then 435 * hb_script_from_iso15924_tag(). 436 * 437 * Return value: 438 * An #hb_script_t corresponding to the ISO15924 tag. 439 * 440 * Since: 0.9.2 441 **/ 442 hb_script_t 443 hb_script_from_string (const char *str, int len) 444 { 445 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len)); 446 } 447 448 /** 449 * hb_script_to_iso15924_tag: 450 * @script: an #hb_script_ to convert. 451 * 452 * See hb_script_from_iso15924_tag(). 453 * 454 * Return value: 455 * An #hb_tag_t representing an ISO15924 script tag. 456 * 457 * Since: 0.9.2 458 **/ 459 hb_tag_t 460 hb_script_to_iso15924_tag (hb_script_t script) 461 { 462 return (hb_tag_t) script; 463 } 464 465 /** 466 * hb_script_get_horizontal_direction: 467 * @script: 468 * 469 * 470 * 471 * Return value: 472 * 473 * Since: 0.9.2 474 **/ 475 hb_direction_t 476 hb_script_get_horizontal_direction (hb_script_t script) 477 { 478 /* http://goo.gl/x9ilM */ 479 switch ((hb_tag_t) script) 480 { 481 /* Unicode-1.1 additions */ 482 case HB_SCRIPT_ARABIC: 483 case HB_SCRIPT_HEBREW: 484 485 /* Unicode-3.0 additions */ 486 case HB_SCRIPT_SYRIAC: 487 case HB_SCRIPT_THAANA: 488 489 /* Unicode-4.0 additions */ 490 case HB_SCRIPT_CYPRIOT: 491 492 /* Unicode-4.1 additions */ 493 case HB_SCRIPT_KHAROSHTHI: 494 495 /* Unicode-5.0 additions */ 496 case HB_SCRIPT_PHOENICIAN: 497 case HB_SCRIPT_NKO: 498 499 /* Unicode-5.1 additions */ 500 case HB_SCRIPT_LYDIAN: 501 502 /* Unicode-5.2 additions */ 503 case HB_SCRIPT_AVESTAN: 504 case HB_SCRIPT_IMPERIAL_ARAMAIC: 505 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: 506 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: 507 case HB_SCRIPT_OLD_SOUTH_ARABIAN: 508 case HB_SCRIPT_OLD_TURKIC: 509 case HB_SCRIPT_SAMARITAN: 510 511 /* Unicode-6.0 additions */ 512 case HB_SCRIPT_MANDAIC: 513 514 /* Unicode-6.1 additions */ 515 case HB_SCRIPT_MEROITIC_CURSIVE: 516 case HB_SCRIPT_MEROITIC_HIEROGLYPHS: 517 518 /* Unicode-7.0 additions */ 519 case HB_SCRIPT_MANICHAEAN: 520 case HB_SCRIPT_MENDE_KIKAKUI: 521 case HB_SCRIPT_NABATAEAN: 522 case HB_SCRIPT_OLD_NORTH_ARABIAN: 523 case HB_SCRIPT_PALMYRENE: 524 case HB_SCRIPT_PSALTER_PAHLAVI: 525 526 /* Unicode-8.0 additions */ 527 case HB_SCRIPT_OLD_HUNGARIAN: 528 529 /* Unicode-9.0 additions */ 530 case HB_SCRIPT_ADLAM: 531 532 return HB_DIRECTION_RTL; 533 } 534 535 return HB_DIRECTION_LTR; 536 } 537 538 539 /* hb_user_data_array_t */ 540 541 bool 542 hb_user_data_array_t::set (hb_user_data_key_t *key, 543 void * data, 544 hb_destroy_func_t destroy, 545 hb_bool_t replace) 546 { 547 if (!key) 548 return false; 549 550 if (replace) { 551 if (!data && !destroy) { 552 items.remove (key, lock); 553 return true; 554 } 555 } 556 hb_user_data_item_t item = {key, data, destroy}; 557 bool ret = !!items.replace_or_insert (item, lock, (bool) replace); 558 559 return ret; 560 } 561 562 void * 563 hb_user_data_array_t::get (hb_user_data_key_t *key) 564 { 565 hb_user_data_item_t item = {nullptr, nullptr, nullptr}; 566 567 return items.find (key, &item, lock) ? item.data : nullptr; 568 } 569 570 571 /* hb_version */ 572 573 /** 574 * hb_version: 575 * @major: (out): Library major version component. 576 * @minor: (out): Library minor version component. 577 * @micro: (out): Library micro version component. 578 * 579 * Returns library version as three integer components. 580 * 581 * Since: 0.9.2 582 **/ 583 void 584 hb_version (unsigned int *major, 585 unsigned int *minor, 586 unsigned int *micro) 587 { 588 *major = HB_VERSION_MAJOR; 589 *minor = HB_VERSION_MINOR; 590 *micro = HB_VERSION_MICRO; 591 } 592 593 /** 594 * hb_version_string: 595 * 596 * Returns library version as a string with three components. 597 * 598 * Return value: library version string. 599 * 600 * Since: 0.9.2 601 **/ 602 const char * 603 hb_version_string (void) 604 { 605 return HB_VERSION_STRING; 606 } 607 608 /** 609 * hb_version_atleast: 610 * @major: 611 * @minor: 612 * @micro: 613 * 614 * 615 * 616 * Return value: 617 * 618 * Since: 0.9.30 619 **/ 620 hb_bool_t 621 hb_version_atleast (unsigned int major, 622 unsigned int minor, 623 unsigned int micro) 624 { 625 return HB_VERSION_ATLEAST (major, minor, micro); 626 } 627 628 629 630 /* hb_feature_t and hb_variation_t */ 631 632 static bool 633 parse_space (const char **pp, const char *end) 634 { 635 while (*pp < end && ISSPACE (**pp)) 636 (*pp)++; 637 return true; 638 } 639 640 static bool 641 parse_char (const char **pp, const char *end, char c) 642 { 643 parse_space (pp, end); 644 645 if (*pp == end || **pp != c) 646 return false; 647 648 (*pp)++; 649 return true; 650 } 651 652 static bool 653 parse_uint (const char **pp, const char *end, unsigned int *pv) 654 { 655 char buf[32]; 656 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); 657 strncpy (buf, *pp, len); 658 buf[len] = '\0'; 659 660 char *p = buf; 661 char *pend = p; 662 unsigned int v; 663 664 /* Intentionally use strtol instead of strtoul, such that 665 * -1 turns into "big number"... */ 666 errno = 0; 667 v = strtol (p, &pend, 0); 668 if (errno || p == pend) 669 return false; 670 671 *pv = v; 672 *pp += pend - p; 673 return true; 674 } 675 676 static bool 677 parse_uint32 (const char **pp, const char *end, uint32_t *pv) 678 { 679 char buf[32]; 680 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); 681 strncpy (buf, *pp, len); 682 buf[len] = '\0'; 683 684 char *p = buf; 685 char *pend = p; 686 unsigned int v; 687 688 /* Intentionally use strtol instead of strtoul, such that 689 * -1 turns into "big number"... */ 690 errno = 0; 691 v = strtol (p, &pend, 0); 692 if (errno || p == pend) 693 return false; 694 695 *pv = v; 696 *pp += pend - p; 697 return true; 698 } 699 700 #if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L) 701 #define USE_XLOCALE 1 702 #define HB_LOCALE_T locale_t 703 #define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr) 704 #define HB_FREE_LOCALE(loc) freelocale (loc) 705 #elif defined(_MSC_VER) 706 #define USE_XLOCALE 1 707 #define HB_LOCALE_T _locale_t 708 #define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName) 709 #define HB_FREE_LOCALE(loc) _free_locale (loc) 710 #define strtod_l(a, b, c) _strtod_l ((a), (b), (c)) 711 #endif 712 713 #ifdef USE_XLOCALE 714 715 static HB_LOCALE_T C_locale; 716 717 #ifdef HB_USE_ATEXIT 718 static void 719 free_C_locale (void) 720 { 721 if (C_locale) 722 HB_FREE_LOCALE (C_locale); 723 } 724 #endif 725 726 static HB_LOCALE_T 727 get_C_locale (void) 728 { 729 retry: 730 HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale); 731 732 if (unlikely (!C)) 733 { 734 C = HB_CREATE_LOCALE ("C"); 735 736 if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C)) 737 { 738 HB_FREE_LOCALE (C_locale); 739 goto retry; 740 } 741 742 #ifdef HB_USE_ATEXIT 743 atexit (free_C_locale); /* First person registers atexit() callback. */ 744 #endif 745 } 746 747 return C; 748 } 749 #endif 750 751 static bool 752 parse_float (const char **pp, const char *end, float *pv) 753 { 754 char buf[32]; 755 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); 756 strncpy (buf, *pp, len); 757 buf[len] = '\0'; 758 759 char *p = buf; 760 char *pend = p; 761 float v; 762 763 errno = 0; 764 #ifdef USE_XLOCALE 765 v = strtod_l (p, &pend, get_C_locale ()); 766 #else 767 v = strtod (p, &pend); 768 #endif 769 if (errno || p == pend) 770 return false; 771 772 *pv = v; 773 *pp += pend - p; 774 return true; 775 } 776 777 static bool 778 parse_bool (const char **pp, const char *end, uint32_t *pv) 779 { 780 parse_space (pp, end); 781 782 const char *p = *pp; 783 while (*pp < end && ISALPHA(**pp)) 784 (*pp)++; 785 786 /* CSS allows on/off as aliases 1/0. */ 787 if (*pp - p == 2 && 0 == strncmp (p, "on", 2)) 788 *pv = 1; 789 else if (*pp - p == 3 && 0 == strncmp (p, "off", 3)) 790 *pv = 0; 791 else 792 return false; 793 794 return true; 795 } 796 797 /* hb_feature_t */ 798 799 static bool 800 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) 801 { 802 if (parse_char (pp, end, '-')) 803 feature->value = 0; 804 else { 805 parse_char (pp, end, '+'); 806 feature->value = 1; 807 } 808 809 return true; 810 } 811 812 static bool 813 parse_tag (const char **pp, const char *end, hb_tag_t *tag) 814 { 815 parse_space (pp, end); 816 817 char quote = 0; 818 819 if (*pp < end && (**pp == '\'' || **pp == '"')) 820 { 821 quote = **pp; 822 (*pp)++; 823 } 824 825 const char *p = *pp; 826 while (*pp < end && ISALNUM(**pp)) 827 (*pp)++; 828 829 if (p == *pp || *pp - p > 4) 830 return false; 831 832 *tag = hb_tag_from_string (p, *pp - p); 833 834 if (quote) 835 { 836 /* CSS expects exactly four bytes. And we only allow quotations for 837 * CSS compatibility. So, enforce the length. */ 838 if (*pp - p != 4) 839 return false; 840 if (*pp == end || **pp != quote) 841 return false; 842 (*pp)++; 843 } 844 845 return true; 846 } 847 848 static bool 849 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) 850 { 851 parse_space (pp, end); 852 853 bool has_start; 854 855 feature->start = 0; 856 feature->end = (unsigned int) -1; 857 858 if (!parse_char (pp, end, '[')) 859 return true; 860 861 has_start = parse_uint (pp, end, &feature->start); 862 863 if (parse_char (pp, end, ':')) { 864 parse_uint (pp, end, &feature->end); 865 } else { 866 if (has_start) 867 feature->end = feature->start + 1; 868 } 869 870 return parse_char (pp, end, ']'); 871 } 872 873 static bool 874 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) 875 { 876 bool had_equal = parse_char (pp, end, '='); 877 bool had_value = parse_uint32 (pp, end, &feature->value) || 878 parse_bool (pp, end, &feature->value); 879 /* CSS doesn't use equal-sign between tag and value. 880 * If there was an equal-sign, then there *must* be a value. 881 * A value without an eqaul-sign is ok, but not required. */ 882 return !had_equal || had_value; 883 } 884 885 static bool 886 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) 887 { 888 return parse_feature_value_prefix (pp, end, feature) && 889 parse_tag (pp, end, &feature->tag) && 890 parse_feature_indices (pp, end, feature) && 891 parse_feature_value_postfix (pp, end, feature) && 892 parse_space (pp, end) && 893 *pp == end; 894 } 895 896 /** 897 * hb_feature_from_string: 898 * @str: (array length=len) (element-type uint8_t): a string to parse 899 * @len: length of @str, or -1 if string is %NULL terminated 900 * @feature: (out): the #hb_feature_t to initialize with the parsed values 901 * 902 * Parses a string into a #hb_feature_t. 903 * 904 * TODO: document the syntax here. 905 * 906 * Return value: 907 * %true if @str is successfully parsed, %false otherwise. 908 * 909 * Since: 0.9.5 910 **/ 911 hb_bool_t 912 hb_feature_from_string (const char *str, int len, 913 hb_feature_t *feature) 914 { 915 hb_feature_t feat; 916 917 if (len < 0) 918 len = strlen (str); 919 920 if (likely (parse_one_feature (&str, str + len, &feat))) 921 { 922 if (feature) 923 *feature = feat; 924 return true; 925 } 926 927 if (feature) 928 memset (feature, 0, sizeof (*feature)); 929 return false; 930 } 931 932 /** 933 * hb_feature_to_string: 934 * @feature: an #hb_feature_t to convert 935 * @buf: (array length=size) (out): output string 936 * @size: the allocated size of @buf 937 * 938 * Converts a #hb_feature_t into a %NULL-terminated string in the format 939 * understood by hb_feature_from_string(). The client in responsible for 940 * allocating big enough size for @buf, 128 bytes is more than enough. 941 * 942 * Since: 0.9.5 943 **/ 944 void 945 hb_feature_to_string (hb_feature_t *feature, 946 char *buf, unsigned int size) 947 { 948 if (unlikely (!size)) return; 949 950 char s[128]; 951 unsigned int len = 0; 952 if (feature->value == 0) 953 s[len++] = '-'; 954 hb_tag_to_string (feature->tag, s + len); 955 len += 4; 956 while (len && s[len - 1] == ' ') 957 len--; 958 if (feature->start != 0 || feature->end != (unsigned int) -1) 959 { 960 s[len++] = '['; 961 if (feature->start) 962 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); 963 if (feature->end != feature->start + 1) { 964 s[len++] = ':'; 965 if (feature->end != (unsigned int) -1) 966 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); 967 } 968 s[len++] = ']'; 969 } 970 if (feature->value > 1) 971 { 972 s[len++] = '='; 973 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); 974 } 975 assert (len < ARRAY_LENGTH (s)); 976 len = MIN (len, size - 1); 977 memcpy (buf, s, len); 978 buf[len] = '\0'; 979 } 980 981 /* hb_variation_t */ 982 983 static bool 984 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation) 985 { 986 parse_char (pp, end, '='); /* Optional. */ 987 return parse_float (pp, end, &variation->value); 988 } 989 990 static bool 991 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation) 992 { 993 return parse_tag (pp, end, &variation->tag) && 994 parse_variation_value (pp, end, variation) && 995 parse_space (pp, end) && 996 *pp == end; 997 } 998 999 /** 1000 * hb_variation_from_string: 1001 * 1002 * Since: 1.4.2 1003 */ 1004 hb_bool_t 1005 hb_variation_from_string (const char *str, int len, 1006 hb_variation_t *variation) 1007 { 1008 hb_variation_t var; 1009 1010 if (len < 0) 1011 len = strlen (str); 1012 1013 if (likely (parse_one_variation (&str, str + len, &var))) 1014 { 1015 if (variation) 1016 *variation = var; 1017 return true; 1018 } 1019 1020 if (variation) 1021 memset (variation, 0, sizeof (*variation)); 1022 return false; 1023 } 1024 1025 /** 1026 * hb_variation_to_string: 1027 * 1028 * Since: 1.4.2 1029 */ 1030 void 1031 hb_variation_to_string (hb_variation_t *variation, 1032 char *buf, unsigned int size) 1033 { 1034 if (unlikely (!size)) return; 1035 1036 char s[128]; 1037 unsigned int len = 0; 1038 hb_tag_to_string (variation->tag, s + len); 1039 len += 4; 1040 while (len && s[len - 1] == ' ') 1041 len--; 1042 s[len++] = '='; 1043 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value)); 1044 1045 assert (len < ARRAY_LENGTH (s)); 1046 len = MIN (len, size - 1); 1047 memcpy (buf, s, len); 1048 buf[len] = '\0'; 1049 } 1050