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"
41 hb_options_union_t _hb_options
;
44 _hb_options_init (void)
48 u
.opts
.initialized
= 1;
50 char *c
= getenv ("HB_OPTIONS");
51 u
.opts
.uniscribe_bug_compatible
= c
&& strstr (c
, "uniscribe-bug-compatible");
53 /* This is idempotent and threadsafe. */
62 * @str: (array length=len):
72 hb_tag_from_string (const char *str
, int len
)
77 if (!str
|| !len
|| !*str
)
80 if (len
< 0 || len
> 4)
82 for (i
= 0; i
< (unsigned) len
&& str
[i
]; i
++)
87 return HB_TAG_CHAR4 (tag
);
93 * @buf: (array fixed-size=4):
100 hb_tag_to_string (hb_tag_t tag
, char *buf
)
102 buf
[0] = (char) (uint8_t) (tag
>> 24);
103 buf
[1] = (char) (uint8_t) (tag
>> 16);
104 buf
[2] = (char) (uint8_t) (tag
>> 8);
105 buf
[3] = (char) (uint8_t) (tag
>> 0);
111 const char direction_strings
[][4] = {
119 * hb_direction_from_string:
120 * @str: (array length=len):
130 hb_direction_from_string (const char *str
, int len
)
132 if (unlikely (!str
|| !len
|| !*str
))
133 return HB_DIRECTION_INVALID
;
135 /* Lets match loosely: just match the first letter, such that
136 * all of "ltr", "left-to-right", etc work!
138 char c
= TOLOWER (str
[0]);
139 for (unsigned int i
= 0; i
< ARRAY_LENGTH (direction_strings
); i
++)
140 if (c
== direction_strings
[i
][0])
141 return (hb_direction_t
) (HB_DIRECTION_LTR
+ i
);
143 return HB_DIRECTION_INVALID
;
147 * hb_direction_to_string:
152 * Return value: (transfer none):
157 hb_direction_to_string (hb_direction_t direction
)
159 if (likely ((unsigned int) (direction
- HB_DIRECTION_LTR
)
160 < ARRAY_LENGTH (direction_strings
)))
161 return direction_strings
[direction
- HB_DIRECTION_LTR
];
169 struct hb_language_impl_t
{
173 static const char canon_map
[256] = {
174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
177 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
178 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
179 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
180 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
181 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
185 lang_equal (hb_language_t v1
,
188 const unsigned char *p1
= (const unsigned char *) v1
;
189 const unsigned char *p2
= (const unsigned char *) v2
;
191 while (*p1
&& *p1
== canon_map
[*p2
])
194 return *p1
== canon_map
[*p2
];
199 lang_hash (const void *key
)
201 const unsigned char *p
= key
;
203 while (canon_map
[*p
])
205 h
= (h
<< 5) - h
+ canon_map
[*p
];
214 struct hb_language_item_t
{
216 struct hb_language_item_t
*next
;
219 inline bool operator == (const char *s
) const {
220 return lang_equal (lang
, s
);
223 inline hb_language_item_t
& operator = (const char *s
) {
224 lang
= (hb_language_t
) strdup (s
);
225 for (unsigned char *p
= (unsigned char *) lang
; *p
; p
++)
231 void finish (void) { free ((void *) lang
); }
235 /* Thread-safe lock-free language list */
237 static hb_language_item_t
*langs
;
240 void free_langs (void)
243 hb_language_item_t
*next
= langs
->next
;
250 static hb_language_item_t
*
251 lang_find_or_insert (const char *key
)
254 hb_language_item_t
*first_lang
= (hb_language_item_t
*) hb_atomic_ptr_get (&langs
);
256 for (hb_language_item_t
*lang
= first_lang
; lang
; lang
= lang
->next
)
260 /* Not found; allocate one. */
261 hb_language_item_t
*lang
= (hb_language_item_t
*) calloc (1, sizeof (hb_language_item_t
));
262 if (unlikely (!lang
))
264 lang
->next
= first_lang
;
267 if (!hb_atomic_ptr_cmpexch (&langs
, first_lang
, lang
)) {
274 atexit (free_langs
); /* First person registers atexit() callback. */
282 * hb_language_from_string:
283 * @str: (array length=len):
293 hb_language_from_string (const char *str
, int len
)
297 if (!str
|| !len
|| !*str
)
298 return HB_LANGUAGE_INVALID
;
302 len
= MIN (len
, (int) sizeof (strbuf
) - 1);
303 str
= (char *) memcpy (strbuf
, str
, len
);
307 hb_language_item_t
*item
= lang_find_or_insert (str
);
309 return likely (item
) ? item
->lang
: HB_LANGUAGE_INVALID
;
313 * hb_language_to_string:
318 * Return value: (transfer none):
323 hb_language_to_string (hb_language_t language
)
325 /* This is actually NULL-safe! */
330 * hb_language_get_default:
339 hb_language_get_default (void)
341 static hb_language_t default_language
= HB_LANGUAGE_INVALID
;
343 hb_language_t language
= (hb_language_t
) hb_atomic_ptr_get (&default_language
);
344 if (unlikely (language
== HB_LANGUAGE_INVALID
)) {
345 language
= hb_language_from_string (setlocale (LC_CTYPE
, NULL
), -1);
346 hb_atomic_ptr_cmpexch (&default_language
, HB_LANGUAGE_INVALID
, language
);
349 return default_language
;
356 * hb_script_from_iso15924_tag:
366 hb_script_from_iso15924_tag (hb_tag_t tag
)
368 if (unlikely (tag
== HB_TAG_NONE
))
369 return HB_SCRIPT_INVALID
;
371 /* Be lenient, adjust case (one capital letter followed by three small letters) */
372 tag
= (tag
& 0xDFDFDFDF) | 0x00202020;
376 /* These graduated from the 'Q' private-area codes, but
377 * the old code is still aliased by Unicode, and the Qaai
378 * one in use by ICU. */
379 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED
;
380 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC
;
382 /* Script variants from http://unicode.org/iso15924/ */
383 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC
;
384 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN
;
385 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN
;
386 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC
;
387 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC
;
388 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC
;
391 /* If it looks right, just use the tag as a script */
392 if (((uint32_t) tag
& 0xE0E0E0E0) == 0x40606060)
393 return (hb_script_t
) tag
;
395 /* Otherwise, return unknown */
396 return HB_SCRIPT_UNKNOWN
;
400 * hb_script_from_string:
401 * @s: (array length=len):
411 hb_script_from_string (const char *s
, int len
)
413 return hb_script_from_iso15924_tag (hb_tag_from_string (s
, len
));
417 * hb_script_to_iso15924_tag:
427 hb_script_to_iso15924_tag (hb_script_t script
)
429 return (hb_tag_t
) script
;
433 * hb_script_get_horizontal_direction:
443 hb_script_get_horizontal_direction (hb_script_t script
)
445 /* http://goo.gl/x9ilM */
446 switch ((hb_tag_t
) script
)
448 /* Unicode-1.1 additions */
449 case HB_SCRIPT_ARABIC
:
450 case HB_SCRIPT_HEBREW
:
452 /* Unicode-3.0 additions */
453 case HB_SCRIPT_SYRIAC
:
454 case HB_SCRIPT_THAANA
:
456 /* Unicode-4.0 additions */
457 case HB_SCRIPT_CYPRIOT
:
459 /* Unicode-4.1 additions */
460 case HB_SCRIPT_KHAROSHTHI
:
462 /* Unicode-5.0 additions */
463 case HB_SCRIPT_PHOENICIAN
:
466 /* Unicode-5.1 additions */
467 case HB_SCRIPT_LYDIAN
:
469 /* Unicode-5.2 additions */
470 case HB_SCRIPT_AVESTAN
:
471 case HB_SCRIPT_IMPERIAL_ARAMAIC
:
472 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI
:
473 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN
:
474 case HB_SCRIPT_OLD_SOUTH_ARABIAN
:
475 case HB_SCRIPT_OLD_TURKIC
:
476 case HB_SCRIPT_SAMARITAN
:
478 /* Unicode-6.0 additions */
479 case HB_SCRIPT_MANDAIC
:
481 /* Unicode-6.1 additions */
482 case HB_SCRIPT_MEROITIC_CURSIVE
:
483 case HB_SCRIPT_MEROITIC_HIEROGLYPHS
:
485 return HB_DIRECTION_RTL
;
488 return HB_DIRECTION_LTR
;
492 /* hb_user_data_array_t */
495 hb_user_data_array_t::set (hb_user_data_key_t
*key
,
497 hb_destroy_func_t destroy
,
504 if (!data
&& !destroy
) {
505 items
.remove (key
, lock
);
509 hb_user_data_item_t item
= {key
, data
, destroy
};
510 bool ret
= !!items
.replace_or_insert (item
, lock
, replace
);
516 hb_user_data_array_t::get (hb_user_data_key_t
*key
)
518 hb_user_data_item_t item
= {NULL
};
520 return items
.find (key
, &item
, lock
) ? item
.data
: NULL
;
528 * @major: (out): Library major version component.
529 * @minor: (out): Library minor version component.
530 * @micro: (out): Library micro version component.
532 * Returns library version as three integer components.
537 hb_version (unsigned int *major
,
541 *major
= HB_VERSION_MAJOR
;
542 *minor
= HB_VERSION_MINOR
;
543 *micro
= HB_VERSION_MICRO
;
549 * Returns library version as a string with three components.
551 * Return value: library version string.
556 hb_version_string (void)
558 return HB_VERSION_STRING
;
574 hb_version_check (unsigned int major
,
578 return HB_VERSION_CHECK (major
, minor
, micro
);