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_static_mutex_t langs_lock;
166 static hb_lockable_set_t<hb_language_item_t, hb_static_mutex_t> langs;
169 hb_language_from_string (const char *str)
174 hb_language_item_t *item = langs.find_or_insert (str, langs_lock);
176 return likely (item) ? item->lang : NULL;
180 hb_language_to_string (hb_language_t language)
186 hb_language_get_default (void)
188 static hb_language_t default_language;
190 if (!default_language) {
191 /* This block is not quite threadsafe, but is not as bad as
192 * it looks since it's idempotent. As long as pointer ops
193 * are atomic, we are safe. */
195 /* I hear that setlocale() doesn't honor env vars on Windows,
196 * but for now we ignore that. */
198 default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL));
201 return default_language;
208 hb_script_from_iso15924_tag (hb_tag_t tag)
210 if (unlikely (tag == HB_TAG_NONE))
211 return HB_SCRIPT_INVALID;
213 /* Be lenient, adjust case (one capital letter followed by three small letters) */
214 tag = (tag & 0xDFDFDFDF) | 0x00202020;
218 /* These graduated from the 'Q' private-area codes, but
219 * the old code is still aliased by Unicode, and the Qaai
220 * one in use by ICU. */
221 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
222 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
224 /* Script variants from http://unicode.org/iso15924/ */
225 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
226 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
227 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
228 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
229 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
230 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
233 /* If it looks right, just use the tag as a script */
234 if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
235 return (hb_script_t) tag;
237 /* Otherwise, return unknown */
238 return HB_SCRIPT_UNKNOWN;
242 hb_script_from_string (const char *s)
244 return hb_script_from_iso15924_tag (hb_tag_from_string (s));
248 hb_script_to_iso15924_tag (hb_script_t script)
250 return (hb_tag_t) script;
254 hb_script_get_horizontal_direction (hb_script_t script)
256 switch ((hb_tag_t) script)
258 case HB_SCRIPT_ARABIC:
259 case HB_SCRIPT_HEBREW:
260 case HB_SCRIPT_SYRIAC:
261 case HB_SCRIPT_THAANA:
263 /* Unicode-4.0 additions */
264 case HB_SCRIPT_CYPRIOT:
266 /* Unicode-5.0 additions */
267 case HB_SCRIPT_PHOENICIAN:
270 /* Unicode-5.2 additions */
271 case HB_SCRIPT_AVESTAN:
272 case HB_SCRIPT_IMPERIAL_ARAMAIC:
273 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
274 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
275 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
276 case HB_SCRIPT_OLD_TURKIC:
277 case HB_SCRIPT_SAMARITAN:
279 /* Unicode-6.0 additions */
280 case HB_SCRIPT_MANDAIC:
282 return HB_DIRECTION_RTL;
285 return HB_DIRECTION_LTR;
289 /* hb_user_data_array_t */
292 /* NOTE: Currently we use a global lock for user_data access
293 * threadsafety. If one day we add a mutex to any object, we
294 * should switch to using that insted for these too.
297 static hb_static_mutex_t user_data_lock;
300 hb_user_data_array_t::set (hb_user_data_key_t *key,
302 hb_destroy_func_t destroy)
307 if (!data && !destroy) {
308 items.remove (key, user_data_lock);
311 hb_user_data_item_t item = {key, data, destroy};
312 bool ret = !!items.replace_or_insert (item, user_data_lock);
318 hb_user_data_array_t::get (hb_user_data_key_t *key)
320 hb_user_data_item_t item = {NULL };
322 return items.find (key, &item, user_data_lock) ? item.data : NULL;
326 hb_user_data_array_t::finish (void)
328 items.finish (user_data_lock);
335 hb_version (unsigned int *major,
339 *major = HB_VERSION_MAJOR;
340 *minor = HB_VERSION_MINOR;
341 *micro = HB_VERSION_MICRO;
345 hb_version_string (void)
347 return HB_VERSION_STRING;
351 hb_version_check (unsigned int major,
355 return HB_VERSION_CHECK (major, minor, micro);