2 * Copyright © 2009,2010 Red Hat, Inc.
3 * Copyright © 2011 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
29 #include "hb-private.hh"
31 #include "hb-version.h"
33 #include "hb-mutex-private.hh"
34 #include "hb-object-private.hh"
44 hb_tag_from_string (const char *s)
52 for (i = 0; i < 4 && s[i]; i++)
57 return HB_TAG_CHAR4 (tag);
63 const char direction_strings[][4] = {
71 hb_direction_from_string (const char *str)
73 if (unlikely (!str || !*str))
74 return HB_DIRECTION_INVALID;
76 /* Lets match loosely: just match the first letter, such that
77 * all of "ltr", "left-to-right", etc work!
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;
84 return HB_DIRECTION_INVALID;
88 hb_direction_to_string (hb_direction_t direction)
90 if (likely ((unsigned int) direction < ARRAY_LENGTH (direction_strings)))
91 return direction_strings[direction];
99 struct _hb_language_t {
103 static 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
115 lang_equal (const void *v1,
118 const unsigned char *p1 = (const unsigned char *) v1;
119 const unsigned char *p2 = (const unsigned char *) v2;
121 while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2])
126 return (canon_map[*p1] == canon_map[*p2]);
131 lang_hash (const void *key)
133 const unsigned char *p = key;
135 while (canon_map[*p])
137 h = (h << 5) - h + canon_map[*p];
146 struct hb_language_item_t {
150 inline bool operator == (const char *s) const {
151 return lang_equal (lang, s);
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++)
162 void finish (void) { free (lang); }
165 static hb_threadsafe_set_t<hb_language_item_t> langs;
168 hb_language_from_string (const char *str)
173 hb_language_item_t *item = langs.find_or_insert (str);
175 return likely (item) ? item->lang : NULL;
179 hb_language_to_string (hb_language_t language)
185 hb_language_get_default (void)
187 static hb_language_t default_language;
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. */
194 /* I hear that setlocale() doesn't honor env vars on Windows,
195 * but for now we ignore that. */
197 default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL));
200 return default_language;
207 hb_script_from_iso15924_tag (hb_tag_t tag)
209 if (unlikely (tag == HB_TAG_NONE))
210 return HB_SCRIPT_INVALID;
212 /* Be lenient, adjust case (one capital letter followed by three small letters) */
213 tag = (tag & 0xDFDFDFDF) | 0x00202020;
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;
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;
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;
236 /* Otherwise, return unknown */
237 return HB_SCRIPT_UNKNOWN;
241 hb_script_from_string (const char *s)
243 return hb_script_from_iso15924_tag (hb_tag_from_string (s));
247 hb_script_to_iso15924_tag (hb_script_t script)
249 return (hb_tag_t) script;
253 hb_script_get_horizontal_direction (hb_script_t script)
255 switch ((hb_tag_t) script)
257 case HB_SCRIPT_ARABIC:
258 case HB_SCRIPT_HEBREW:
259 case HB_SCRIPT_SYRIAC:
260 case HB_SCRIPT_THAANA:
262 /* Unicode-4.0 additions */
263 case HB_SCRIPT_CYPRIOT:
265 /* Unicode-5.0 additions */
266 case HB_SCRIPT_PHOENICIAN:
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:
278 /* Unicode-6.0 additions */
279 case HB_SCRIPT_MANDAIC:
281 return HB_DIRECTION_RTL;
284 return HB_DIRECTION_LTR;
288 /* hb_user_data_array_t */
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.
296 static hb_static_mutex_t user_data_mutex;
299 hb_user_data_array_t::set (hb_user_data_key_t *key,
301 hb_destroy_func_t destroy)
306 hb_mutex_lock (&user_data_mutex);
308 if (!data && !destroy) {
312 hb_user_data_item_t item = {key, data, destroy};
313 bool ret = !!items.insert (item);
315 hb_mutex_unlock (&user_data_mutex);
321 hb_user_data_array_t::get (hb_user_data_key_t *key)
323 hb_mutex_lock (&user_data_mutex);
325 hb_user_data_item_t *item = items.find (key);
326 void *ret = item ? item->data : NULL;
328 hb_mutex_unlock (&user_data_mutex);
337 hb_version (unsigned int *major,
341 *major = HB_VERSION_MAJOR;
342 *minor = HB_VERSION_MINOR;
343 *micro = HB_VERSION_MICRO;
347 hb_version_string (void)
349 return HB_VERSION_STRING;
353 hb_version_check (unsigned int major,
357 return HB_VERSION_CHECK (major, minor, micro);