hb-common.cc revision 45bfa99034512e886d75b1d45a5a649647f4711f
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_threadsafe_set_t<hb_language_item_t> langs;
166
167hb_language_t
168hb_language_from_string (const char *str)
169{
170  if (!str || !*str)
171    return NULL;
172
173  hb_language_item_t *item = langs.find_or_insert (str);
174
175  return likely (item) ? item->lang : NULL;
176}
177
178const char *
179hb_language_to_string (hb_language_t language)
180{
181  return language->s;
182}
183
184hb_language_t
185hb_language_get_default (void)
186{
187  static hb_language_t default_language;
188
189  if (!default_language) {
190    /* This block is not quite threadsafe, but is not as bad as
191     * it looks since it's idempotent.  As long as pointer ops
192     * are atomic, we are safe. */
193
194    /* I hear that setlocale() doesn't honor env vars on Windows,
195     * but for now we ignore that. */
196
197    default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL));
198  }
199
200  return default_language;
201}
202
203
204/* hb_script_t */
205
206hb_script_t
207hb_script_from_iso15924_tag (hb_tag_t tag)
208{
209  if (unlikely (tag == HB_TAG_NONE))
210    return HB_SCRIPT_INVALID;
211
212  /* Be lenient, adjust case (one capital letter followed by three small letters) */
213  tag = (tag & 0xDFDFDFDF) | 0x00202020;
214
215  switch (tag) {
216
217    /* These graduated from the 'Q' private-area codes, but
218     * the old code is still aliased by Unicode, and the Qaai
219     * one in use by ICU. */
220    case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
221    case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
222
223    /* Script variants from http://unicode.org/iso15924/ */
224    case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
225    case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
226    case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
227    case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
228    case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
229    case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
230  }
231
232  /* If it looks right, just use the tag as a script */
233  if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
234    return (hb_script_t) tag;
235
236  /* Otherwise, return unknown */
237  return HB_SCRIPT_UNKNOWN;
238}
239
240hb_script_t
241hb_script_from_string (const char *s)
242{
243  return hb_script_from_iso15924_tag (hb_tag_from_string (s));
244}
245
246hb_tag_t
247hb_script_to_iso15924_tag (hb_script_t script)
248{
249  return (hb_tag_t) script;
250}
251
252hb_direction_t
253hb_script_get_horizontal_direction (hb_script_t script)
254{
255  switch ((hb_tag_t) script)
256  {
257    case HB_SCRIPT_ARABIC:
258    case HB_SCRIPT_HEBREW:
259    case HB_SCRIPT_SYRIAC:
260    case HB_SCRIPT_THAANA:
261
262    /* Unicode-4.0 additions */
263    case HB_SCRIPT_CYPRIOT:
264
265    /* Unicode-5.0 additions */
266    case HB_SCRIPT_PHOENICIAN:
267    case HB_SCRIPT_NKO:
268
269    /* Unicode-5.2 additions */
270    case HB_SCRIPT_AVESTAN:
271    case HB_SCRIPT_IMPERIAL_ARAMAIC:
272    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
273    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
274    case HB_SCRIPT_OLD_SOUTH_ARABIAN:
275    case HB_SCRIPT_OLD_TURKIC:
276    case HB_SCRIPT_SAMARITAN:
277
278    /* Unicode-6.0 additions */
279    case HB_SCRIPT_MANDAIC:
280
281      return HB_DIRECTION_RTL;
282  }
283
284  return HB_DIRECTION_LTR;
285}
286
287
288/* hb_user_data_array_t */
289
290
291/* NOTE: Currently we use a global lock for user_data access
292 * threadsafety.  If one day we add a mutex to any object, we
293 * should switch to using that insted for these too.
294 */
295
296static hb_static_mutex_t user_data_lock;
297
298bool
299hb_user_data_array_t::set (hb_user_data_key_t *key,
300			   void *              data,
301			   hb_destroy_func_t   destroy)
302{
303  if (!key)
304    return false;
305
306  if (!data && !destroy) {
307    items.remove (key, user_data_lock);
308    return true;
309  }
310  hb_user_data_item_t item = {key, data, destroy};
311  bool ret = !!items.replace_or_insert (item, user_data_lock);
312
313  return ret;
314}
315
316void *
317hb_user_data_array_t::get (hb_user_data_key_t *key)
318{
319  hb_user_data_item_t item = {NULL };
320
321  return items.find (key, &item, user_data_lock) ? item.data : NULL;
322}
323
324void
325hb_user_data_array_t::finish (void)
326{
327  items.finish (user_data_lock);
328}
329
330
331/* hb_version */
332
333void
334hb_version (unsigned int *major,
335	    unsigned int *minor,
336	    unsigned int *micro)
337{
338  *major = HB_VERSION_MAJOR;
339  *minor = HB_VERSION_MINOR;
340  *micro = HB_VERSION_MICRO;
341}
342
343const char *
344hb_version_string (void)
345{
346  return HB_VERSION_STRING;
347}
348
349hb_bool_t
350hb_version_check (unsigned int major,
351		  unsigned int minor,
352		  unsigned int micro)
353{
354  return HB_VERSION_CHECK (major, minor, micro);
355}
356
357
358HB_END_DECLS
359