Home | History | Annotate | Download | only in src
      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