2 * Copyright © 2009,2010 Red Hat, Inc.
3 * Copyright © 2011,2012 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"
43 hb_tag_from_string (const char *s, int len)
48 if (!s || !len || !*s)
51 if (len < 0 || len > 4)
53 for (i = 0; i < (unsigned) len && s[i]; i++)
58 return HB_TAG_CHAR4 (tag);
64 const char direction_strings[][4] = {
72 hb_direction_from_string (const char *str, int len)
74 if (unlikely (!str || !len || !*str))
75 return HB_DIRECTION_INVALID;
77 /* Lets match loosely: just match the first letter, such that
78 * all of "ltr", "left-to-right", etc work!
80 char c = TOLOWER (str[0]);
81 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
82 if (c == direction_strings[i][0])
83 return (hb_direction_t) (HB_DIRECTION_LTR + i);
85 return HB_DIRECTION_INVALID;
89 hb_direction_to_string (hb_direction_t direction)
91 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
92 < ARRAY_LENGTH (direction_strings)))
93 return direction_strings[direction - HB_DIRECTION_LTR];
101 struct hb_language_impl_t {
105 static const char canon_map[256] = {
106 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
109 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
110 '-', '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, '-',
112 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
113 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
117 lang_equal (hb_language_t v1,
120 const unsigned char *p1 = (const unsigned char *) v1;
121 const unsigned char *p2 = (const unsigned char *) v2;
123 while (*p1 && *p1 == canon_map[*p2])
126 return *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 {
148 struct hb_language_item_t *next;
151 inline bool operator == (const char *s) const {
152 return lang_equal (lang, s);
155 inline hb_language_item_t & operator = (const char *s) {
156 lang = (hb_language_t) strdup (s);
157 for (unsigned char *p = (unsigned char *) lang; *p; p++)
163 void finish (void) { free (lang); }
167 /* Thread-safe lock-free language list */
169 static hb_language_item_t *langs;
172 void free_langs (void)
175 hb_language_item_t *next = langs->next;
182 static hb_language_item_t *
183 lang_find_or_insert (const char *key)
186 hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
188 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
192 /* Not found; allocate one. */
193 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
194 if (unlikely (!lang))
196 lang->next = first_lang;
199 if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
206 atexit (free_langs); /* First person registers atexit() callback. */
214 hb_language_from_string (const char *str, int len)
216 if (!str || !len || !*str)
217 return HB_LANGUAGE_INVALID;
221 len = MIN (len, (int) sizeof (strbuf) - 1);
222 str = (char *) memcpy (strbuf, str, len);
226 hb_language_item_t *item = lang_find_or_insert (str);
228 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
232 hb_language_to_string (hb_language_t language)
234 /* This is actually NULL-safe! */
239 hb_language_get_default (void)
241 static hb_language_t default_language = HB_LANGUAGE_INVALID;
243 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
244 if (unlikely (language == HB_LANGUAGE_INVALID)) {
245 language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
246 hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
249 return default_language;
256 hb_script_from_iso15924_tag (hb_tag_t tag)
258 if (unlikely (tag == HB_TAG_NONE))
259 return HB_SCRIPT_INVALID;
261 /* Be lenient, adjust case (one capital letter followed by three small letters) */
262 tag = (tag & 0xDFDFDFDF) | 0x00202020;
266 /* These graduated from the 'Q' private-area codes, but
267 * the old code is still aliased by Unicode, and the Qaai
268 * one in use by ICU. */
269 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
270 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
272 /* Script variants from http://unicode.org/iso15924/ */
273 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
274 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
275 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
276 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
277 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
278 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
281 /* If it looks right, just use the tag as a script */
282 if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
283 return (hb_script_t) tag;
285 /* Otherwise, return unknown */
286 return HB_SCRIPT_UNKNOWN;
290 hb_script_from_string (const char *s, int len)
292 return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
296 hb_script_to_iso15924_tag (hb_script_t script)
298 return (hb_tag_t) script;
302 hb_script_get_horizontal_direction (hb_script_t script)
304 /* http://goo.gl/x9ilM */
305 switch ((hb_tag_t) script)
307 /* Unicode-1.1 additions */
308 case HB_SCRIPT_ARABIC:
309 case HB_SCRIPT_HEBREW:
311 /* Unicode-3.0 additions */
312 case HB_SCRIPT_SYRIAC:
313 case HB_SCRIPT_THAANA:
315 /* Unicode-4.0 additions */
316 case HB_SCRIPT_CYPRIOT:
318 /* Unicode-4.1 additions */
319 case HB_SCRIPT_KHAROSHTHI:
321 /* Unicode-5.0 additions */
322 case HB_SCRIPT_PHOENICIAN:
325 /* Unicode-5.1 additions */
326 case HB_SCRIPT_LYDIAN:
328 /* Unicode-5.2 additions */
329 case HB_SCRIPT_AVESTAN:
330 case HB_SCRIPT_IMPERIAL_ARAMAIC:
331 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
332 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
333 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
334 case HB_SCRIPT_OLD_TURKIC:
335 case HB_SCRIPT_SAMARITAN:
337 /* Unicode-6.0 additions */
338 case HB_SCRIPT_MANDAIC:
340 /* Unicode-6.1 additions */
341 case HB_SCRIPT_MEROITIC_CURSIVE:
342 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
344 return HB_DIRECTION_RTL;
347 return HB_DIRECTION_LTR;
351 /* hb_user_data_array_t */
354 hb_user_data_array_t::set (hb_user_data_key_t *key,
356 hb_destroy_func_t destroy,
364 if (!data && !destroy) {
365 items.remove (key, lock);
369 hb_user_data_item_t item = {key, data, destroy};
370 bool ret = !!items.replace_or_insert (item, lock, replace);
376 hb_user_data_array_t::get (hb_user_data_key_t *key,
379 hb_user_data_item_t item = {NULL };
381 return items.find (key, &item, lock) ? item.data : NULL;
385 hb_user_data_array_t::finish (hb_mutex_t &lock)
394 hb_version (unsigned int *major,
398 *major = HB_VERSION_MAJOR;
399 *minor = HB_VERSION_MINOR;
400 *micro = HB_VERSION_MICRO;
404 hb_version_string (void)
406 return HB_VERSION_STRING;
410 hb_version_check (unsigned int major,
414 return HB_VERSION_CHECK (major, minor, micro);