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
);
62 hb_tag_to_string (hb_tag_t tag
, char *buf
)
64 buf
[0] = (char) (uint8_t) (tag
>> 24);
65 buf
[1] = (char) (uint8_t) (tag
>> 16);
66 buf
[2] = (char) (uint8_t) (tag
>> 8);
67 buf
[3] = (char) (uint8_t) (tag
>> 0);
73 const char direction_strings
[][4] = {
81 hb_direction_from_string (const char *str
, int len
)
83 if (unlikely (!str
|| !len
|| !*str
))
84 return HB_DIRECTION_INVALID
;
86 /* Lets match loosely: just match the first letter, such that
87 * all of "ltr", "left-to-right", etc work!
89 char c
= TOLOWER (str
[0]);
90 for (unsigned int i
= 0; i
< ARRAY_LENGTH (direction_strings
); i
++)
91 if (c
== direction_strings
[i
][0])
92 return (hb_direction_t
) (HB_DIRECTION_LTR
+ i
);
94 return HB_DIRECTION_INVALID
;
98 hb_direction_to_string (hb_direction_t direction
)
100 if (likely ((unsigned int) (direction
- HB_DIRECTION_LTR
)
101 < ARRAY_LENGTH (direction_strings
)))
102 return direction_strings
[direction
- HB_DIRECTION_LTR
];
110 struct hb_language_impl_t
{
114 static const char canon_map
[256] = {
115 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
116 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
118 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
119 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
120 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
121 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
122 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
126 lang_equal (hb_language_t v1
,
129 const unsigned char *p1
= (const unsigned char *) v1
;
130 const unsigned char *p2
= (const unsigned char *) v2
;
132 while (*p1
&& *p1
== canon_map
[*p2
])
135 return *p1
== canon_map
[*p2
];
140 lang_hash (const void *key
)
142 const unsigned char *p
= key
;
144 while (canon_map
[*p
])
146 h
= (h
<< 5) - h
+ canon_map
[*p
];
155 struct hb_language_item_t
{
157 struct hb_language_item_t
*next
;
160 inline bool operator == (const char *s
) const {
161 return lang_equal (lang
, s
);
164 inline hb_language_item_t
& operator = (const char *s
) {
165 lang
= (hb_language_t
) strdup (s
);
166 for (unsigned char *p
= (unsigned char *) lang
; *p
; p
++)
172 void finish (void) { free (lang
); }
176 /* Thread-safe lock-free language list */
178 static hb_language_item_t
*langs
;
181 void free_langs (void)
184 hb_language_item_t
*next
= langs
->next
;
191 static hb_language_item_t
*
192 lang_find_or_insert (const char *key
)
195 hb_language_item_t
*first_lang
= (hb_language_item_t
*) hb_atomic_ptr_get (&langs
);
197 for (hb_language_item_t
*lang
= first_lang
; lang
; lang
= lang
->next
)
201 /* Not found; allocate one. */
202 hb_language_item_t
*lang
= (hb_language_item_t
*) calloc (1, sizeof (hb_language_item_t
));
203 if (unlikely (!lang
))
205 lang
->next
= first_lang
;
208 if (!hb_atomic_ptr_cmpexch (&langs
, first_lang
, lang
)) {
215 atexit (free_langs
); /* First person registers atexit() callback. */
223 hb_language_from_string (const char *str
, int len
)
225 if (!str
|| !len
|| !*str
)
226 return HB_LANGUAGE_INVALID
;
230 len
= MIN (len
, (int) sizeof (strbuf
) - 1);
231 str
= (char *) memcpy (strbuf
, str
, len
);
235 hb_language_item_t
*item
= lang_find_or_insert (str
);
237 return likely (item
) ? item
->lang
: HB_LANGUAGE_INVALID
;
241 hb_language_to_string (hb_language_t language
)
243 /* This is actually NULL-safe! */
248 hb_language_get_default (void)
250 static hb_language_t default_language
= HB_LANGUAGE_INVALID
;
252 hb_language_t language
= (hb_language_t
) hb_atomic_ptr_get (&default_language
);
253 if (unlikely (language
== HB_LANGUAGE_INVALID
)) {
254 language
= hb_language_from_string (setlocale (LC_CTYPE
, NULL
), -1);
255 hb_atomic_ptr_cmpexch (&default_language
, HB_LANGUAGE_INVALID
, language
);
258 return default_language
;
265 hb_script_from_iso15924_tag (hb_tag_t tag
)
267 if (unlikely (tag
== HB_TAG_NONE
))
268 return HB_SCRIPT_INVALID
;
270 /* Be lenient, adjust case (one capital letter followed by three small letters) */
271 tag
= (tag
& 0xDFDFDFDF) | 0x00202020;
275 /* These graduated from the 'Q' private-area codes, but
276 * the old code is still aliased by Unicode, and the Qaai
277 * one in use by ICU. */
278 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED
;
279 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC
;
281 /* Script variants from http://unicode.org/iso15924/ */
282 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC
;
283 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN
;
284 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN
;
285 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC
;
286 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC
;
287 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC
;
290 /* If it looks right, just use the tag as a script */
291 if (((uint32_t) tag
& 0xE0E0E0E0) == 0x40606060)
292 return (hb_script_t
) tag
;
294 /* Otherwise, return unknown */
295 return HB_SCRIPT_UNKNOWN
;
299 hb_script_from_string (const char *s
, int len
)
301 return hb_script_from_iso15924_tag (hb_tag_from_string (s
, len
));
305 hb_script_to_iso15924_tag (hb_script_t script
)
307 return (hb_tag_t
) script
;
311 hb_script_get_horizontal_direction (hb_script_t script
)
313 /* http://goo.gl/x9ilM */
314 switch ((hb_tag_t
) script
)
316 /* Unicode-1.1 additions */
317 case HB_SCRIPT_ARABIC
:
318 case HB_SCRIPT_HEBREW
:
320 /* Unicode-3.0 additions */
321 case HB_SCRIPT_SYRIAC
:
322 case HB_SCRIPT_THAANA
:
324 /* Unicode-4.0 additions */
325 case HB_SCRIPT_CYPRIOT
:
327 /* Unicode-4.1 additions */
328 case HB_SCRIPT_KHAROSHTHI
:
330 /* Unicode-5.0 additions */
331 case HB_SCRIPT_PHOENICIAN
:
334 /* Unicode-5.1 additions */
335 case HB_SCRIPT_LYDIAN
:
337 /* Unicode-5.2 additions */
338 case HB_SCRIPT_AVESTAN
:
339 case HB_SCRIPT_IMPERIAL_ARAMAIC
:
340 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI
:
341 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN
:
342 case HB_SCRIPT_OLD_SOUTH_ARABIAN
:
343 case HB_SCRIPT_OLD_TURKIC
:
344 case HB_SCRIPT_SAMARITAN
:
346 /* Unicode-6.0 additions */
347 case HB_SCRIPT_MANDAIC
:
349 /* Unicode-6.1 additions */
350 case HB_SCRIPT_MEROITIC_CURSIVE
:
351 case HB_SCRIPT_MEROITIC_HIEROGLYPHS
:
353 return HB_DIRECTION_RTL
;
356 return HB_DIRECTION_LTR
;
360 /* hb_user_data_array_t */
363 hb_user_data_array_t::set (hb_user_data_key_t
*key
,
365 hb_destroy_func_t destroy
,
373 if (!data
&& !destroy
) {
374 items
.remove (key
, lock
);
378 hb_user_data_item_t item
= {key
, data
, destroy
};
379 bool ret
= !!items
.replace_or_insert (item
, lock
, replace
);
385 hb_user_data_array_t::get (hb_user_data_key_t
*key
,
388 hb_user_data_item_t item
= {NULL
};
390 return items
.find (key
, &item
, lock
) ? item
.data
: NULL
;
394 hb_user_data_array_t::finish (hb_mutex_t
&lock
)
403 hb_version (unsigned int *major
,
407 *major
= HB_VERSION_MAJOR
;
408 *minor
= HB_VERSION_MINOR
;
409 *micro
= HB_VERSION_MICRO
;
413 hb_version_string (void)
415 return HB_VERSION_STRING
;
419 hb_version_check (unsigned int major
,
423 return HB_VERSION_CHECK (major
, minor
, micro
);