hb-common.cc revision 45bfa99034512e886d75b1d45a5a649647f4711f
1/* 2 * Copyright © 2009,2010 Red Hat, Inc. 3 * Copyright © 2011 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-version.h" 32 33#include "hb-mutex-private.hh" 34#include "hb-object-private.hh" 35 36#include <locale.h> 37 38HB_BEGIN_DECLS 39 40 41/* hb_tag_t */ 42 43hb_tag_t 44hb_tag_from_string (const char *s) 45{ 46 char tag[4]; 47 unsigned int i; 48 49 if (!s || !*s) 50 return HB_TAG_NONE; 51 52 for (i = 0; i < 4 && s[i]; i++) 53 tag[i] = s[i]; 54 for (; i < 4; i++) 55 tag[i] = ' '; 56 57 return HB_TAG_CHAR4 (tag); 58} 59 60 61/* hb_direction_t */ 62 63const char direction_strings[][4] = { 64 "ltr", 65 "rtl", 66 "ttb", 67 "btt" 68}; 69 70hb_direction_t 71hb_direction_from_string (const char *str) 72{ 73 if (unlikely (!str || !*str)) 74 return HB_DIRECTION_INVALID; 75 76 /* Lets match loosely: just match the first letter, such that 77 * all of "ltr", "left-to-right", etc work! 78 */ 79 char c = TOLOWER (str[0]); 80 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++) 81 if (c == direction_strings[i][0]) 82 return (hb_direction_t) i; 83 84 return HB_DIRECTION_INVALID; 85} 86 87const char * 88hb_direction_to_string (hb_direction_t direction) 89{ 90 if (likely ((unsigned int) direction < ARRAY_LENGTH (direction_strings))) 91 return direction_strings[direction]; 92 93 return "invalid"; 94} 95 96 97/* hb_language_t */ 98 99struct _hb_language_t { 100 const char s[1]; 101}; 102 103static const char canon_map[256] = { 104 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 107 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, 108 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 109 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', 110 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 111 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 112}; 113 114static hb_bool_t 115lang_equal (const void *v1, 116 const void *v2) 117{ 118 const unsigned char *p1 = (const unsigned char *) v1; 119 const unsigned char *p2 = (const unsigned char *) v2; 120 121 while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2]) 122 { 123 p1++, p2++; 124 } 125 126 return (canon_map[*p1] == canon_map[*p2]); 127} 128 129#if 0 130static unsigned int 131lang_hash (const void *key) 132{ 133 const unsigned char *p = key; 134 unsigned int h = 0; 135 while (canon_map[*p]) 136 { 137 h = (h << 5) - h + canon_map[*p]; 138 p++; 139 } 140 141 return h; 142} 143#endif 144 145 146struct hb_language_item_t { 147 148 hb_language_t lang; 149 150 inline bool operator == (const char *s) const { 151 return lang_equal (lang, s); 152 } 153 154 inline hb_language_item_t & operator = (const char *s) { 155 lang = (hb_language_t) strdup (s); 156 for (unsigned char *p = (unsigned char *) lang; *p; p++) 157 *p = canon_map[*p]; 158 159 return *this; 160 } 161 162 void finish (void) { free (lang); } 163}; 164 165static hb_static_threadsafe_set_t<hb_language_item_t> langs; 166 167hb_language_t 168hb_language_from_string (const char *str) 169{ 170 if (!str || !*str) 171 return NULL; 172 173 hb_language_item_t *item = langs.find_or_insert (str); 174 175 return likely (item) ? item->lang : NULL; 176} 177 178const char * 179hb_language_to_string (hb_language_t language) 180{ 181 return language->s; 182} 183 184hb_language_t 185hb_language_get_default (void) 186{ 187 static hb_language_t default_language; 188 189 if (!default_language) { 190 /* This block is not quite threadsafe, but is not as bad as 191 * it looks since it's idempotent. As long as pointer ops 192 * are atomic, we are safe. */ 193 194 /* I hear that setlocale() doesn't honor env vars on Windows, 195 * but for now we ignore that. */ 196 197 default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL)); 198 } 199 200 return default_language; 201} 202 203 204/* hb_script_t */ 205 206hb_script_t 207hb_script_from_iso15924_tag (hb_tag_t tag) 208{ 209 if (unlikely (tag == HB_TAG_NONE)) 210 return HB_SCRIPT_INVALID; 211 212 /* Be lenient, adjust case (one capital letter followed by three small letters) */ 213 tag = (tag & 0xDFDFDFDF) | 0x00202020; 214 215 switch (tag) { 216 217 /* These graduated from the 'Q' private-area codes, but 218 * the old code is still aliased by Unicode, and the Qaai 219 * one in use by ICU. */ 220 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; 221 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; 222 223 /* Script variants from http://unicode.org/iso15924/ */ 224 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; 225 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; 226 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; 227 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; 228 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC; 229 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC; 230 } 231 232 /* If it looks right, just use the tag as a script */ 233 if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060) 234 return (hb_script_t) tag; 235 236 /* Otherwise, return unknown */ 237 return HB_SCRIPT_UNKNOWN; 238} 239 240hb_script_t 241hb_script_from_string (const char *s) 242{ 243 return hb_script_from_iso15924_tag (hb_tag_from_string (s)); 244} 245 246hb_tag_t 247hb_script_to_iso15924_tag (hb_script_t script) 248{ 249 return (hb_tag_t) script; 250} 251 252hb_direction_t 253hb_script_get_horizontal_direction (hb_script_t script) 254{ 255 switch ((hb_tag_t) script) 256 { 257 case HB_SCRIPT_ARABIC: 258 case HB_SCRIPT_HEBREW: 259 case HB_SCRIPT_SYRIAC: 260 case HB_SCRIPT_THAANA: 261 262 /* Unicode-4.0 additions */ 263 case HB_SCRIPT_CYPRIOT: 264 265 /* Unicode-5.0 additions */ 266 case HB_SCRIPT_PHOENICIAN: 267 case HB_SCRIPT_NKO: 268 269 /* Unicode-5.2 additions */ 270 case HB_SCRIPT_AVESTAN: 271 case HB_SCRIPT_IMPERIAL_ARAMAIC: 272 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: 273 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: 274 case HB_SCRIPT_OLD_SOUTH_ARABIAN: 275 case HB_SCRIPT_OLD_TURKIC: 276 case HB_SCRIPT_SAMARITAN: 277 278 /* Unicode-6.0 additions */ 279 case HB_SCRIPT_MANDAIC: 280 281 return HB_DIRECTION_RTL; 282 } 283 284 return HB_DIRECTION_LTR; 285} 286 287 288/* hb_user_data_array_t */ 289 290 291/* NOTE: Currently we use a global lock for user_data access 292 * threadsafety. If one day we add a mutex to any object, we 293 * should switch to using that insted for these too. 294 */ 295 296static hb_static_mutex_t user_data_lock; 297 298bool 299hb_user_data_array_t::set (hb_user_data_key_t *key, 300 void * data, 301 hb_destroy_func_t destroy) 302{ 303 if (!key) 304 return false; 305 306 if (!data && !destroy) { 307 items.remove (key, user_data_lock); 308 return true; 309 } 310 hb_user_data_item_t item = {key, data, destroy}; 311 bool ret = !!items.replace_or_insert (item, user_data_lock); 312 313 return ret; 314} 315 316void * 317hb_user_data_array_t::get (hb_user_data_key_t *key) 318{ 319 hb_user_data_item_t item = {NULL }; 320 321 return items.find (key, &item, user_data_lock) ? item.data : NULL; 322} 323 324void 325hb_user_data_array_t::finish (void) 326{ 327 items.finish (user_data_lock); 328} 329 330 331/* hb_version */ 332 333void 334hb_version (unsigned int *major, 335 unsigned int *minor, 336 unsigned int *micro) 337{ 338 *major = HB_VERSION_MAJOR; 339 *minor = HB_VERSION_MINOR; 340 *micro = HB_VERSION_MICRO; 341} 342 343const char * 344hb_version_string (void) 345{ 346 return HB_VERSION_STRING; 347} 348 349hb_bool_t 350hb_version_check (unsigned int major, 351 unsigned int minor, 352 unsigned int micro) 353{ 354 return HB_VERSION_CHECK (major, minor, micro); 355} 356 357 358HB_END_DECLS 359