hb-common.cc revision 389a7c9e67549b1a9f7c538965e4647077f8e6ec
1/*
2 * Copyright © 2009,2010  Red Hat, Inc.
3 * Copyright © 2011  Google, Inc.
4 *
5 *  This is part of HarfBuzz, a text shaping library.
6 *
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.
12 *
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
17 * DAMAGE.
18 *
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.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
27 */
28
29#include "hb-private.hh"
30
31#include "hb-version.h"
32
33#include "hb-mutex-private.hh"
34#include "hb-object-private.hh"
35
36#include <locale.h>
37
38HB_BEGIN_DECLS
39
40
41/* hb_tag_t */
42
43hb_tag_t
44hb_tag_from_string (const char *s)
45{
46  char tag[4];
47  unsigned int i;
48
49  if (!s || !*s)
50    return HB_TAG_NONE;
51
52  for (i = 0; i < 4 && s[i]; i++)
53    tag[i] = s[i];
54  for (; i < 4; i++)
55    tag[i] = ' ';
56
57  return HB_TAG_CHAR4 (tag);
58}
59
60
61/* hb_direction_t */
62
63const char direction_strings[][4] = {
64  "ltr",
65  "rtl",
66  "ttb",
67  "btt"
68};
69
70hb_direction_t
71hb_direction_from_string (const char *str)
72{
73  if (unlikely (!str || !*str))
74    return HB_DIRECTION_INVALID;
75
76  /* Lets match loosely: just match the first letter, such that
77   * all of "ltr", "left-to-right", etc work!
78   */
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;
83
84  return HB_DIRECTION_INVALID;
85}
86
87const char *
88hb_direction_to_string (hb_direction_t direction)
89{
90  if (likely ((unsigned int) direction < ARRAY_LENGTH (direction_strings)))
91    return direction_strings[direction];
92
93  return "invalid";
94}
95
96
97/* hb_language_t */
98
99struct _hb_language_t {
100  const char s[1];
101};
102
103static 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
112};
113
114static hb_bool_t
115lang_equal (const void *v1,
116	    const void *v2)
117{
118  const unsigned char *p1 = (const unsigned char *) v1;
119  const unsigned char *p2 = (const unsigned char *) v2;
120
121  while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2])
122    {
123      p1++, p2++;
124    }
125
126  return (canon_map[*p1] == canon_map[*p2]);
127}
128
129#if 0
130static unsigned int
131lang_hash (const void *key)
132{
133  const unsigned char *p = key;
134  unsigned int h = 0;
135  while (canon_map[*p])
136    {
137      h = (h << 5) - h + canon_map[*p];
138      p++;
139    }
140
141  return h;
142}
143#endif
144
145
146struct hb_language_item_t {
147
148  hb_language_t lang;
149
150  inline bool operator == (const char *s) const {
151    return lang_equal (lang, s);
152  }
153
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++)
157      *p = canon_map[*p];
158
159    return *this;
160  }
161
162  void finish (void) { free (lang); }
163};
164
165static hb_static_mutex_t langs_lock;
166static hb_lockable_set_t<hb_language_item_t, hb_static_mutex_t> langs;
167
168hb_language_t
169hb_language_from_string (const char *str)
170{
171  if (!str || !*str)
172    return NULL;
173
174  hb_language_item_t *item = langs.find_or_insert (str, langs_lock);
175
176  return likely (item) ? item->lang : NULL;
177}
178
179const char *
180hb_language_to_string (hb_language_t language)
181{
182  return language->s;
183}
184
185hb_language_t
186hb_language_get_default (void)
187{
188  static hb_language_t default_language;
189
190  if (!default_language) {
191    /* This block is not quite threadsafe, but is not as bad as
192     * it looks since it's idempotent.  As long as pointer ops
193     * are atomic, we are safe. */
194
195    /* I hear that setlocale() doesn't honor env vars on Windows,
196     * but for now we ignore that. */
197
198    default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL));
199  }
200
201  return default_language;
202}
203
204
205/* hb_script_t */
206
207hb_script_t
208hb_script_from_iso15924_tag (hb_tag_t tag)
209{
210  if (unlikely (tag == HB_TAG_NONE))
211    return HB_SCRIPT_INVALID;
212
213  /* Be lenient, adjust case (one capital letter followed by three small letters) */
214  tag = (tag & 0xDFDFDFDF) | 0x00202020;
215
216  switch (tag) {
217
218    /* These graduated from the 'Q' private-area codes, but
219     * the old code is still aliased by Unicode, and the Qaai
220     * one in use by ICU. */
221    case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
222    case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
223
224    /* Script variants from http://unicode.org/iso15924/ */
225    case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
226    case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
227    case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
228    case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
229    case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
230    case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
231  }
232
233  /* If it looks right, just use the tag as a script */
234  if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
235    return (hb_script_t) tag;
236
237  /* Otherwise, return unknown */
238  return HB_SCRIPT_UNKNOWN;
239}
240
241hb_script_t
242hb_script_from_string (const char *s)
243{
244  return hb_script_from_iso15924_tag (hb_tag_from_string (s));
245}
246
247hb_tag_t
248hb_script_to_iso15924_tag (hb_script_t script)
249{
250  return (hb_tag_t) script;
251}
252
253hb_direction_t
254hb_script_get_horizontal_direction (hb_script_t script)
255{
256  switch ((hb_tag_t) script)
257  {
258    case HB_SCRIPT_ARABIC:
259    case HB_SCRIPT_HEBREW:
260    case HB_SCRIPT_SYRIAC:
261    case HB_SCRIPT_THAANA:
262
263    /* Unicode-4.0 additions */
264    case HB_SCRIPT_CYPRIOT:
265
266    /* Unicode-5.0 additions */
267    case HB_SCRIPT_PHOENICIAN:
268    case HB_SCRIPT_NKO:
269
270    /* Unicode-5.2 additions */
271    case HB_SCRIPT_AVESTAN:
272    case HB_SCRIPT_IMPERIAL_ARAMAIC:
273    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
274    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
275    case HB_SCRIPT_OLD_SOUTH_ARABIAN:
276    case HB_SCRIPT_OLD_TURKIC:
277    case HB_SCRIPT_SAMARITAN:
278
279    /* Unicode-6.0 additions */
280    case HB_SCRIPT_MANDAIC:
281
282      return HB_DIRECTION_RTL;
283  }
284
285  return HB_DIRECTION_LTR;
286}
287
288
289/* hb_user_data_array_t */
290
291
292/* NOTE: Currently we use a global lock for user_data access
293 * threadsafety.  If one day we add a mutex to any object, we
294 * should switch to using that insted for these too.
295 */
296
297static hb_static_mutex_t user_data_lock;
298
299bool
300hb_user_data_array_t::set (hb_user_data_key_t *key,
301			   void *              data,
302			   hb_destroy_func_t   destroy)
303{
304  if (!key)
305    return false;
306
307  if (!data && !destroy) {
308    items.remove (key, user_data_lock);
309    return true;
310  }
311  hb_user_data_item_t item = {key, data, destroy};
312  bool ret = !!items.replace_or_insert (item, user_data_lock);
313
314  return ret;
315}
316
317void *
318hb_user_data_array_t::get (hb_user_data_key_t *key)
319{
320  hb_user_data_item_t item = {NULL };
321
322  return items.find (key, &item, user_data_lock) ? item.data : NULL;
323}
324
325void
326hb_user_data_array_t::finish (void)
327{
328  items.finish (user_data_lock);
329}
330
331
332/* hb_version */
333
334void
335hb_version (unsigned int *major,
336	    unsigned int *minor,
337	    unsigned int *micro)
338{
339  *major = HB_VERSION_MAJOR;
340  *minor = HB_VERSION_MINOR;
341  *micro = HB_VERSION_MICRO;
342}
343
344const char *
345hb_version_string (void)
346{
347  return HB_VERSION_STRING;
348}
349
350hb_bool_t
351hb_version_check (unsigned int major,
352		  unsigned int minor,
353		  unsigned int micro)
354{
355  return HB_VERSION_CHECK (major, minor, micro);
356}
357
358
359HB_END_DECLS
360