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)
172 return HB_LANGUAGE_INVALID;
174 hb_language_item_t *item = langs.find_or_insert (str, langs_lock);
176 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
180 hb_language_to_string (hb_language_t language)
182 /* This is actually NULL-safe! */
187 hb_language_get_default (void)
189 static hb_language_t default_language;
191 if (!default_language) {
192 /* This block is not quite threadsafe, but is not as bad as
193 * it looks since it's idempotent. As long as pointer ops
194 * are atomic, we are safe. */
196 /* I hear that setlocale() doesn't honor env vars on Windows,
197 * but for now we ignore that. */
199 default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL));
202 return default_language;
209 hb_script_from_iso15924_tag (hb_tag_t tag)
211 if (unlikely (tag == HB_TAG_NONE))
212 return HB_SCRIPT_INVALID;
214 /* Be lenient, adjust case (one capital letter followed by three small letters) */
215 tag = (tag & 0xDFDFDFDF) | 0x00202020;
219 /* These graduated from the 'Q' private-area codes, but
220 * the old code is still aliased by Unicode, and the Qaai
221 * one in use by ICU. */
222 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
223 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
225 /* Script variants from http://unicode.org/iso15924/ */
226 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
227 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
228 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
229 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
230 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
231 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
234 /* If it looks right, just use the tag as a script */
235 if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
236 return (hb_script_t) tag;
238 /* Otherwise, return unknown */
239 return HB_SCRIPT_UNKNOWN;
243 hb_script_from_string (const char *s)
245 return hb_script_from_iso15924_tag (hb_tag_from_string (s));
249 hb_script_to_iso15924_tag (hb_script_t script)
251 return (hb_tag_t) script;
255 hb_script_get_horizontal_direction (hb_script_t script)
257 switch ((hb_tag_t) script)
259 case HB_SCRIPT_ARABIC:
260 case HB_SCRIPT_HEBREW:
261 case HB_SCRIPT_SYRIAC:
262 case HB_SCRIPT_THAANA:
264 /* Unicode-4.0 additions */
265 case HB_SCRIPT_CYPRIOT:
267 /* Unicode-5.0 additions */
268 case HB_SCRIPT_PHOENICIAN:
271 /* Unicode-5.2 additions */
272 case HB_SCRIPT_AVESTAN:
273 case HB_SCRIPT_IMPERIAL_ARAMAIC:
274 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
275 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
276 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
277 case HB_SCRIPT_OLD_TURKIC:
278 case HB_SCRIPT_SAMARITAN:
280 /* Unicode-6.0 additions */
281 case HB_SCRIPT_MANDAIC:
283 return HB_DIRECTION_RTL;
286 return HB_DIRECTION_LTR;
290 /* hb_user_data_array_t */
293 /* NOTE: Currently we use a global lock for user_data access
294 * threadsafety. If one day we add a mutex to any object, we
295 * should switch to using that insted for these too.
298 static hb_static_mutex_t user_data_lock;
301 hb_user_data_array_t::set (hb_user_data_key_t *key,
303 hb_destroy_func_t destroy)
308 if (!data && !destroy) {
309 items.remove (key, user_data_lock);
312 hb_user_data_item_t item = {key, data, destroy};
313 bool ret = !!items.replace_or_insert (item, user_data_lock);
319 hb_user_data_array_t::get (hb_user_data_key_t *key)
321 hb_user_data_item_t item = {NULL };
323 return items.find (key, &item, user_data_lock) ? item.data : NULL;
327 hb_user_data_array_t::finish (void)
329 items.finish (user_data_lock);
336 hb_version (unsigned int *major,
340 *major = HB_VERSION_MAJOR;
341 *minor = HB_VERSION_MINOR;
342 *micro = HB_VERSION_MICRO;
346 hb_version_string (void)
348 return HB_VERSION_STRING;
352 hb_version_check (unsigned int major,
356 return HB_VERSION_CHECK (major, minor, micro);