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 36 37 /* hb_options_t */ 38 39 hb_options_union_t _hb_options; 40 41 void 42 _hb_options_init (void) 43 { 44 hb_options_union_t u; 45 u.i = 0; 46 u.opts.initialized = 1; 47 48 char *c = getenv ("HB_OPTIONS"); 49 u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible"); 50 51 /* This is idempotent and threadsafe. */ 52 _hb_options = u; 53 } 54 55 56 /* hb_tag_t */ 57 58 /** 59 * hb_tag_from_string: 60 * @str: (array length=len): 61 * @len: 62 * 63 * 64 * 65 * Return value: 66 * 67 * Since: 1.0 68 **/ 69 hb_tag_t 70 hb_tag_from_string (const char *str, int len) 71 { 72 char tag[4]; 73 unsigned int i; 74 75 if (!str || !len || !*str) 76 return HB_TAG_NONE; 77 78 if (len < 0 || len > 4) 79 len = 4; 80 for (i = 0; i < (unsigned) len && str[i]; i++) 81 tag[i] = str[i]; 82 for (; i < 4; i++) 83 tag[i] = ' '; 84 85 return HB_TAG_CHAR4 (tag); 86 } 87 88 /** 89 * hb_tag_to_string: 90 * @tag: 91 * @buf: (array fixed-size=4): 92 * 93 * 94 * 95 * Since: 1.0 96 **/ 97 void 98 hb_tag_to_string (hb_tag_t tag, char *buf) 99 { 100 buf[0] = (char) (uint8_t) (tag >> 24); 101 buf[1] = (char) (uint8_t) (tag >> 16); 102 buf[2] = (char) (uint8_t) (tag >> 8); 103 buf[3] = (char) (uint8_t) (tag >> 0); 104 } 105 106 107 /* hb_direction_t */ 108 109 const char direction_strings[][4] = { 110 "ltr", 111 "rtl", 112 "ttb", 113 "btt" 114 }; 115 116 /** 117 * hb_direction_from_string: 118 * @str: (array length=len): 119 * @len: 120 * 121 * 122 * 123 * Return value: 124 * 125 * Since: 1.0 126 **/ 127 hb_direction_t 128 hb_direction_from_string (const char *str, int len) 129 { 130 if (unlikely (!str || !len || !*str)) 131 return HB_DIRECTION_INVALID; 132 133 /* Lets match loosely: just match the first letter, such that 134 * all of "ltr", "left-to-right", etc work! 135 */ 136 char c = TOLOWER (str[0]); 137 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++) 138 if (c == direction_strings[i][0]) 139 return (hb_direction_t) (HB_DIRECTION_LTR + i); 140 141 return HB_DIRECTION_INVALID; 142 } 143 144 /** 145 * hb_direction_to_string: 146 * @direction: 147 * 148 * 149 * 150 * Return value: (transfer none): 151 * 152 * Since: 1.0 153 **/ 154 const char * 155 hb_direction_to_string (hb_direction_t direction) 156 { 157 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR) 158 < ARRAY_LENGTH (direction_strings))) 159 return direction_strings[direction - HB_DIRECTION_LTR]; 160 161 return "invalid"; 162 } 163 164 165 /* hb_language_t */ 166 167 struct hb_language_impl_t { 168 const char s[1]; 169 }; 170 171 static const char canon_map[256] = { 172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 175 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, 176 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 177 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', 178 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 179 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 180 }; 181 182 static hb_bool_t 183 lang_equal (hb_language_t v1, 184 const void *v2) 185 { 186 const unsigned char *p1 = (const unsigned char *) v1; 187 const unsigned char *p2 = (const unsigned char *) v2; 188 189 while (*p1 && *p1 == canon_map[*p2]) 190 p1++, p2++; 191 192 return *p1 == canon_map[*p2]; 193 } 194 195 #if 0 196 static unsigned int 197 lang_hash (const void *key) 198 { 199 const unsigned char *p = key; 200 unsigned int h = 0; 201 while (canon_map[*p]) 202 { 203 h = (h << 5) - h + canon_map[*p]; 204 p++; 205 } 206 207 return h; 208 } 209 #endif 210 211 212 struct hb_language_item_t { 213 214 struct hb_language_item_t *next; 215 hb_language_t lang; 216 217 inline bool operator == (const char *s) const { 218 return lang_equal (lang, s); 219 } 220 221 inline hb_language_item_t & operator = (const char *s) { 222 lang = (hb_language_t) strdup (s); 223 for (unsigned char *p = (unsigned char *) lang; *p; p++) 224 *p = canon_map[*p]; 225 226 return *this; 227 } 228 229 void finish (void) { free ((void *) lang); } 230 }; 231 232 233 /* Thread-safe lock-free language list */ 234 235 static hb_language_item_t *langs; 236 237 #ifdef HAVE_ATEXIT 238 static inline 239 void free_langs (void) 240 { 241 while (langs) { 242 hb_language_item_t *next = langs->next; 243 langs->finish (); 244 free (langs); 245 langs = next; 246 } 247 } 248 #endif 249 250 static hb_language_item_t * 251 lang_find_or_insert (const char *key) 252 { 253 retry: 254 hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs); 255 256 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next) 257 if (*lang == key) 258 return lang; 259 260 /* Not found; allocate one. */ 261 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); 262 if (unlikely (!lang)) 263 return NULL; 264 lang->next = first_lang; 265 *lang = key; 266 267 if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) { 268 free (lang); 269 goto retry; 270 } 271 272 #ifdef HAVE_ATEXIT 273 if (!first_lang) 274 atexit (free_langs); /* First person registers atexit() callback. */ 275 #endif 276 277 return lang; 278 } 279 280 281 /** 282 * hb_language_from_string: 283 * @str: (array length=len): 284 * @len: 285 * 286 * 287 * 288 * Return value: 289 * 290 * Since: 1.0 291 **/ 292 hb_language_t 293 hb_language_from_string (const char *str, int len) 294 { 295 char strbuf[64]; 296 297 if (!str || !len || !*str) 298 return HB_LANGUAGE_INVALID; 299 300 if (len >= 0) 301 { 302 len = MIN (len, (int) sizeof (strbuf) - 1); 303 str = (char *) memcpy (strbuf, str, len); 304 strbuf[len] = '\0'; 305 } 306 307 hb_language_item_t *item = lang_find_or_insert (str); 308 309 return likely (item) ? item->lang : HB_LANGUAGE_INVALID; 310 } 311 312 /** 313 * hb_language_to_string: 314 * @language: 315 * 316 * 317 * 318 * Return value: (transfer none): 319 * 320 * Since: 1.0 321 **/ 322 const char * 323 hb_language_to_string (hb_language_t language) 324 { 325 /* This is actually NULL-safe! */ 326 return language->s; 327 } 328 329 /** 330 * hb_language_get_default: 331 * 332 * 333 * 334 * Return value: 335 * 336 * Since: 1.0 337 **/ 338 hb_language_t 339 hb_language_get_default (void) 340 { 341 static hb_language_t default_language = HB_LANGUAGE_INVALID; 342 343 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language); 344 if (unlikely (language == HB_LANGUAGE_INVALID)) { 345 language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1); 346 hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language); 347 } 348 349 return default_language; 350 } 351 352 353 /* hb_script_t */ 354 355 /** 356 * hb_script_from_iso15924_tag: 357 * @tag: 358 * 359 * 360 * 361 * Return value: 362 * 363 * Since: 1.0 364 **/ 365 hb_script_t 366 hb_script_from_iso15924_tag (hb_tag_t tag) 367 { 368 if (unlikely (tag == HB_TAG_NONE)) 369 return HB_SCRIPT_INVALID; 370 371 /* Be lenient, adjust case (one capital letter followed by three small letters) */ 372 tag = (tag & 0xDFDFDFDF) | 0x00202020; 373 374 switch (tag) { 375 376 /* These graduated from the 'Q' private-area codes, but 377 * the old code is still aliased by Unicode, and the Qaai 378 * one in use by ICU. */ 379 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; 380 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; 381 382 /* Script variants from http://unicode.org/iso15924/ */ 383 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; 384 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; 385 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; 386 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; 387 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC; 388 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC; 389 } 390 391 /* If it looks right, just use the tag as a script */ 392 if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060) 393 return (hb_script_t) tag; 394 395 /* Otherwise, return unknown */ 396 return HB_SCRIPT_UNKNOWN; 397 } 398 399 /** 400 * hb_script_from_string: 401 * @s: (array length=len): 402 * @len: 403 * 404 * 405 * 406 * Return value: 407 * 408 * Since: 1.0 409 **/ 410 hb_script_t 411 hb_script_from_string (const char *s, int len) 412 { 413 return hb_script_from_iso15924_tag (hb_tag_from_string (s, len)); 414 } 415 416 /** 417 * hb_script_to_iso15924_tag: 418 * @script: 419 * 420 * 421 * 422 * Return value: 423 * 424 * Since: 1.0 425 **/ 426 hb_tag_t 427 hb_script_to_iso15924_tag (hb_script_t script) 428 { 429 return (hb_tag_t) script; 430 } 431 432 /** 433 * hb_script_get_horizontal_direction: 434 * @script: 435 * 436 * 437 * 438 * Return value: 439 * 440 * Since: 1.0 441 **/ 442 hb_direction_t 443 hb_script_get_horizontal_direction (hb_script_t script) 444 { 445 /* http://goo.gl/x9ilM */ 446 switch ((hb_tag_t) script) 447 { 448 /* Unicode-1.1 additions */ 449 case HB_SCRIPT_ARABIC: 450 case HB_SCRIPT_HEBREW: 451 452 /* Unicode-3.0 additions */ 453 case HB_SCRIPT_SYRIAC: 454 case HB_SCRIPT_THAANA: 455 456 /* Unicode-4.0 additions */ 457 case HB_SCRIPT_CYPRIOT: 458 459 /* Unicode-4.1 additions */ 460 case HB_SCRIPT_KHAROSHTHI: 461 462 /* Unicode-5.0 additions */ 463 case HB_SCRIPT_PHOENICIAN: 464 case HB_SCRIPT_NKO: 465 466 /* Unicode-5.1 additions */ 467 case HB_SCRIPT_LYDIAN: 468 469 /* Unicode-5.2 additions */ 470 case HB_SCRIPT_AVESTAN: 471 case HB_SCRIPT_IMPERIAL_ARAMAIC: 472 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: 473 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: 474 case HB_SCRIPT_OLD_SOUTH_ARABIAN: 475 case HB_SCRIPT_OLD_TURKIC: 476 case HB_SCRIPT_SAMARITAN: 477 478 /* Unicode-6.0 additions */ 479 case HB_SCRIPT_MANDAIC: 480 481 /* Unicode-6.1 additions */ 482 case HB_SCRIPT_MEROITIC_CURSIVE: 483 case HB_SCRIPT_MEROITIC_HIEROGLYPHS: 484 485 return HB_DIRECTION_RTL; 486 } 487 488 return HB_DIRECTION_LTR; 489 } 490 491 492 /* hb_user_data_array_t */ 493 494 bool 495 hb_user_data_array_t::set (hb_user_data_key_t *key, 496 void * data, 497 hb_destroy_func_t destroy, 498 hb_bool_t replace) 499 { 500 if (!key) 501 return false; 502 503 if (replace) { 504 if (!data && !destroy) { 505 items.remove (key, lock); 506 return true; 507 } 508 } 509 hb_user_data_item_t item = {key, data, destroy}; 510 bool ret = !!items.replace_or_insert (item, lock, replace); 511 512 return ret; 513 } 514 515 void * 516 hb_user_data_array_t::get (hb_user_data_key_t *key) 517 { 518 hb_user_data_item_t item = {NULL }; 519 520 return items.find (key, &item, lock) ? item.data : NULL; 521 } 522 523 524 /* hb_version */ 525 526 /** 527 * hb_version: 528 * @major: (out): Library major version component. 529 * @minor: (out): Library minor version component. 530 * @micro: (out): Library micro version component. 531 * 532 * Returns library version as three integer components. 533 * 534 * Since: 1.0 535 **/ 536 void 537 hb_version (unsigned int *major, 538 unsigned int *minor, 539 unsigned int *micro) 540 { 541 *major = HB_VERSION_MAJOR; 542 *minor = HB_VERSION_MINOR; 543 *micro = HB_VERSION_MICRO; 544 } 545 546 /** 547 * hb_version_string: 548 * 549 * Returns library version as a string with three components. 550 * 551 * Return value: library version string. 552 * 553 * Since: 1.0 554 **/ 555 const char * 556 hb_version_string (void) 557 { 558 return HB_VERSION_STRING; 559 } 560 561 /** 562 * hb_version_check: 563 * @major: 564 * @minor: 565 * @micro: 566 * 567 * 568 * 569 * Return value: 570 * 571 * Since: 1.0 572 **/ 573 hb_bool_t 574 hb_version_check (unsigned int major, 575 unsigned int minor, 576 unsigned int micro) 577 { 578 return HB_VERSION_CHECK (major, minor, micro); 579 } 580