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_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;
243 if (!default_language) {
244 /* This block is not quite threadsafe, but is not as bad as
245 * it looks since it's idempotent. As long as pointer ops
246 * are atomic, we are safe. */
248 /* I hear that setlocale() doesn't honor env vars on Windows,
249 * but for now we ignore that. */
251 default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
254 return default_language;
261 hb_script_from_iso15924_tag (hb_tag_t tag)
263 if (unlikely (tag == HB_TAG_NONE))
264 return HB_SCRIPT_INVALID;
266 /* Be lenient, adjust case (one capital letter followed by three small letters) */
267 tag = (tag & 0xDFDFDFDF) | 0x00202020;
271 /* These graduated from the 'Q' private-area codes, but
272 * the old code is still aliased by Unicode, and the Qaai
273 * one in use by ICU. */
274 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
275 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
277 /* Script variants from http://unicode.org/iso15924/ */
278 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
279 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
280 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
281 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
282 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
283 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
286 /* If it looks right, just use the tag as a script */
287 if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
288 return (hb_script_t) tag;
290 /* Otherwise, return unknown */
291 return HB_SCRIPT_UNKNOWN;
295 hb_script_from_string (const char *s, int len)
297 return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
301 hb_script_to_iso15924_tag (hb_script_t script)
303 return (hb_tag_t) script;
307 hb_script_get_horizontal_direction (hb_script_t script)
309 /* http://goo.gl/x9ilM */
310 switch ((hb_tag_t) script)
312 /* Unicode-1.1 additions */
313 case HB_SCRIPT_ARABIC:
314 case HB_SCRIPT_HEBREW:
316 /* Unicode-3.0 additions */
317 case HB_SCRIPT_SYRIAC:
318 case HB_SCRIPT_THAANA:
320 /* Unicode-4.0 additions */
321 case HB_SCRIPT_CYPRIOT:
323 /* Unicode-4.1 additions */
324 case HB_SCRIPT_KHAROSHTHI:
326 /* Unicode-5.0 additions */
327 case HB_SCRIPT_PHOENICIAN:
330 /* Unicode-5.1 additions */
331 case HB_SCRIPT_LYDIAN:
333 /* Unicode-5.2 additions */
334 case HB_SCRIPT_AVESTAN:
335 case HB_SCRIPT_IMPERIAL_ARAMAIC:
336 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
337 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
338 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
339 case HB_SCRIPT_OLD_TURKIC:
340 case HB_SCRIPT_SAMARITAN:
342 /* Unicode-6.0 additions */
343 case HB_SCRIPT_MANDAIC:
345 /* Unicode-6.1 additions */
346 case HB_SCRIPT_MEROITIC_CURSIVE:
347 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
349 return HB_DIRECTION_RTL;
352 return HB_DIRECTION_LTR;
356 /* hb_user_data_array_t */
359 hb_user_data_array_t::set (hb_user_data_key_t *key,
361 hb_destroy_func_t destroy,
369 if (!data && !destroy) {
370 items.remove (key, lock);
374 hb_user_data_item_t item = {key, data, destroy};
375 bool ret = !!items.replace_or_insert (item, lock, replace);
381 hb_user_data_array_t::get (hb_user_data_key_t *key,
384 hb_user_data_item_t item = {NULL };
386 return items.find (key, &item, lock) ? item.data : NULL;
390 hb_user_data_array_t::finish (hb_mutex_t &lock)
399 hb_version (unsigned int *major,
403 *major = HB_VERSION_MAJOR;
404 *minor = HB_VERSION_MINOR;
405 *micro = HB_VERSION_MICRO;
409 hb_version_string (void)
411 return HB_VERSION_STRING;
415 hb_version_check (unsigned int major,
419 return HB_VERSION_CHECK (major, minor, micro);