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     /* NUL-terminate it. */
    303     len = MIN (len, (int) sizeof (strbuf) - 1);
    304     memcpy (strbuf, str, len);
    305     strbuf[len] = '\0';
    306     str = strbuf;
    307   }
    308 
    309   hb_language_item_t *item = lang_find_or_insert (str);
    310 
    311   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
    312 }
    313 
    314 /**
    315  * hb_language_to_string:
    316  * @language:
    317  *
    318  *
    319  *
    320  * Return value: (transfer none):
    321  *
    322  * Since: 1.0
    323  **/
    324 const char *
    325 hb_language_to_string (hb_language_t language)
    326 {
    327   /* This is actually NULL-safe! */
    328   return language->s;
    329 }
    330 
    331 /**
    332  * hb_language_get_default:
    333  *
    334  *
    335  *
    336  * Return value:
    337  *
    338  * Since: 1.0
    339  **/
    340 hb_language_t
    341 hb_language_get_default (void)
    342 {
    343   static hb_language_t default_language = HB_LANGUAGE_INVALID;
    344 
    345   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
    346   if (unlikely (language == HB_LANGUAGE_INVALID)) {
    347     language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
    348     hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
    349   }
    350 
    351   return default_language;
    352 }
    353 
    354 
    355 /* hb_script_t */
    356 
    357 /**
    358  * hb_script_from_iso15924_tag:
    359  * @tag:
    360  *
    361  *
    362  *
    363  * Return value:
    364  *
    365  * Since: 1.0
    366  **/
    367 hb_script_t
    368 hb_script_from_iso15924_tag (hb_tag_t tag)
    369 {
    370   if (unlikely (tag == HB_TAG_NONE))
    371     return HB_SCRIPT_INVALID;
    372 
    373   /* Be lenient, adjust case (one capital letter followed by three small letters) */
    374   tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
    375 
    376   switch (tag) {
    377 
    378     /* These graduated from the 'Q' private-area codes, but
    379      * the old code is still aliased by Unicode, and the Qaai
    380      * one in use by ICU. */
    381     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
    382     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
    383 
    384     /* Script variants from http://unicode.org/iso15924/ */
    385     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
    386     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
    387     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
    388     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
    389     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
    390     case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
    391   }
    392 
    393   /* If it looks right, just use the tag as a script */
    394   if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
    395     return (hb_script_t) tag;
    396 
    397   /* Otherwise, return unknown */
    398   return HB_SCRIPT_UNKNOWN;
    399 }
    400 
    401 /**
    402  * hb_script_from_string:
    403  * @s: (array length=len):
    404  * @len:
    405  *
    406  *
    407  *
    408  * Return value:
    409  *
    410  * Since: 1.0
    411  **/
    412 hb_script_t
    413 hb_script_from_string (const char *s, int len)
    414 {
    415   return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
    416 }
    417 
    418 /**
    419  * hb_script_to_iso15924_tag:
    420  * @script:
    421  *
    422  *
    423  *
    424  * Return value:
    425  *
    426  * Since: 1.0
    427  **/
    428 hb_tag_t
    429 hb_script_to_iso15924_tag (hb_script_t script)
    430 {
    431   return (hb_tag_t) script;
    432 }
    433 
    434 /**
    435  * hb_script_get_horizontal_direction:
    436  * @script:
    437  *
    438  *
    439  *
    440  * Return value:
    441  *
    442  * Since: 1.0
    443  **/
    444 hb_direction_t
    445 hb_script_get_horizontal_direction (hb_script_t script)
    446 {
    447   /* http://goo.gl/x9ilM */
    448   switch ((hb_tag_t) script)
    449   {
    450     /* Unicode-1.1 additions */
    451     case HB_SCRIPT_ARABIC:
    452     case HB_SCRIPT_HEBREW:
    453 
    454     /* Unicode-3.0 additions */
    455     case HB_SCRIPT_SYRIAC:
    456     case HB_SCRIPT_THAANA:
    457 
    458     /* Unicode-4.0 additions */
    459     case HB_SCRIPT_CYPRIOT:
    460 
    461     /* Unicode-4.1 additions */
    462     case HB_SCRIPT_KHAROSHTHI:
    463 
    464     /* Unicode-5.0 additions */
    465     case HB_SCRIPT_PHOENICIAN:
    466     case HB_SCRIPT_NKO:
    467 
    468     /* Unicode-5.1 additions */
    469     case HB_SCRIPT_LYDIAN:
    470 
    471     /* Unicode-5.2 additions */
    472     case HB_SCRIPT_AVESTAN:
    473     case HB_SCRIPT_IMPERIAL_ARAMAIC:
    474     case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
    475     case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
    476     case HB_SCRIPT_OLD_SOUTH_ARABIAN:
    477     case HB_SCRIPT_OLD_TURKIC:
    478     case HB_SCRIPT_SAMARITAN:
    479 
    480     /* Unicode-6.0 additions */
    481     case HB_SCRIPT_MANDAIC:
    482 
    483     /* Unicode-6.1 additions */
    484     case HB_SCRIPT_MEROITIC_CURSIVE:
    485     case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
    486 
    487     /* Unicode-7.0 additions */
    488     case HB_SCRIPT_MANICHAEAN:
    489     case HB_SCRIPT_MENDE_KIKAKUI:
    490     case HB_SCRIPT_NABATAEAN:
    491     case HB_SCRIPT_OLD_NORTH_ARABIAN:
    492     case HB_SCRIPT_PALMYRENE:
    493     case HB_SCRIPT_PSALTER_PAHLAVI:
    494 
    495       return HB_DIRECTION_RTL;
    496   }
    497 
    498   return HB_DIRECTION_LTR;
    499 }
    500 
    501 
    502 /* hb_user_data_array_t */
    503 
    504 bool
    505 hb_user_data_array_t::set (hb_user_data_key_t *key,
    506 			   void *              data,
    507 			   hb_destroy_func_t   destroy,
    508 			   hb_bool_t           replace)
    509 {
    510   if (!key)
    511     return false;
    512 
    513   if (replace) {
    514     if (!data && !destroy) {
    515       items.remove (key, lock);
    516       return true;
    517     }
    518   }
    519   hb_user_data_item_t item = {key, data, destroy};
    520   bool ret = !!items.replace_or_insert (item, lock, replace);
    521 
    522   return ret;
    523 }
    524 
    525 void *
    526 hb_user_data_array_t::get (hb_user_data_key_t *key)
    527 {
    528   hb_user_data_item_t item = {NULL };
    529 
    530   return items.find (key, &item, lock) ? item.data : NULL;
    531 }
    532 
    533 
    534 /* hb_version */
    535 
    536 /**
    537  * hb_version:
    538  * @major: (out): Library major version component.
    539  * @minor: (out): Library minor version component.
    540  * @micro: (out): Library micro version component.
    541  *
    542  * Returns library version as three integer components.
    543  *
    544  * Since: 1.0
    545  **/
    546 void
    547 hb_version (unsigned int *major,
    548 	    unsigned int *minor,
    549 	    unsigned int *micro)
    550 {
    551   *major = HB_VERSION_MAJOR;
    552   *minor = HB_VERSION_MINOR;
    553   *micro = HB_VERSION_MICRO;
    554 }
    555 
    556 /**
    557  * hb_version_string:
    558  *
    559  * Returns library version as a string with three components.
    560  *
    561  * Return value: library version string.
    562  *
    563  * Since: 1.0
    564  **/
    565 const char *
    566 hb_version_string (void)
    567 {
    568   return HB_VERSION_STRING;
    569 }
    570 
    571 /**
    572  * hb_version_atleast:
    573  * @major:
    574  * @minor:
    575  * @micro:
    576  *
    577  *
    578  *
    579  * Return value:
    580  *
    581  * Since: 1.0
    582  **/
    583 hb_bool_t
    584 hb_version_atleast (unsigned int major,
    585 		    unsigned int minor,
    586 		    unsigned int micro)
    587 {
    588   return HB_VERSION_ATLEAST (major, minor, micro);
    589 }
    590