• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2009,2010  Red Hat, Inc.
3  * Copyright © 2011,2012  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 
38 
39 /* hb_options_t */
40 
41 hb_options_union_t _hb_options;
42 
43 void
_hb_options_init(void)44 _hb_options_init (void)
45 {
46   hb_options_union_t u;
47   u.i = 0;
48   u.opts.initialized = 1;
49 
50   char *c = getenv ("HB_OPTIONS");
51   u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
52 
53   /* This is idempotent and threadsafe. */
54   _hb_options = u;
55 }
56 
57 
58 /* hb_tag_t */
59 
60 /**
61  * hb_tag_from_string:
62  * @str: (array length=len):
63  * @len:
64  *
65  *
66  *
67  * Return value:
68  *
69  * Since: 1.0
70  **/
71 hb_tag_t
hb_tag_from_string(const char * str,int len)72 hb_tag_from_string (const char *str, int len)
73 {
74   char tag[4];
75   unsigned int i;
76 
77   if (!str || !len || !*str)
78     return HB_TAG_NONE;
79 
80   if (len < 0 || len > 4)
81     len = 4;
82   for (i = 0; i < (unsigned) len && str[i]; i++)
83     tag[i] = str[i];
84   for (; i < 4; i++)
85     tag[i] = ' ';
86 
87   return HB_TAG_CHAR4 (tag);
88 }
89 
90 /**
91  * hb_tag_to_string:
92  * @tag:
93  * @buf: (array fixed-size=4):
94  *
95  *
96  *
97  * Since: 1.0
98  **/
99 void
hb_tag_to_string(hb_tag_t tag,char * buf)100 hb_tag_to_string (hb_tag_t tag, char *buf)
101 {
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);
106 }
107 
108 
109 /* hb_direction_t */
110 
111 const char direction_strings[][4] = {
112   "ltr",
113   "rtl",
114   "ttb",
115   "btt"
116 };
117 
118 /**
119  * hb_direction_from_string:
120  * @str: (array length=len):
121  * @len:
122  *
123  *
124  *
125  * Return value:
126  *
127  * Since: 1.0
128  **/
129 hb_direction_t
hb_direction_from_string(const char * str,int len)130 hb_direction_from_string (const char *str, int len)
131 {
132   if (unlikely (!str || !len || !*str))
133     return HB_DIRECTION_INVALID;
134 
135   /* Lets match loosely: just match the first letter, such that
136    * all of "ltr", "left-to-right", etc work!
137    */
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);
142 
143   return HB_DIRECTION_INVALID;
144 }
145 
146 /**
147  * hb_direction_to_string:
148  * @direction:
149  *
150  *
151  *
152  * Return value: (transfer none):
153  *
154  * Since: 1.0
155  **/
156 const char *
hb_direction_to_string(hb_direction_t direction)157 hb_direction_to_string (hb_direction_t direction)
158 {
159   if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
160 	      < ARRAY_LENGTH (direction_strings)))
161     return direction_strings[direction - HB_DIRECTION_LTR];
162 
163   return "invalid";
164 }
165 
166 
167 /* hb_language_t */
168 
169 struct hb_language_impl_t {
170   const char s[1];
171 };
172 
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
182 };
183 
184 static hb_bool_t
lang_equal(hb_language_t v1,const void * v2)185 lang_equal (hb_language_t  v1,
186 	    const void    *v2)
187 {
188   const unsigned char *p1 = (const unsigned char *) v1;
189   const unsigned char *p2 = (const unsigned char *) v2;
190 
191   while (*p1 && *p1 == canon_map[*p2])
192     p1++, p2++;
193 
194   return *p1 == canon_map[*p2];
195 }
196 
197 #if 0
198 static unsigned int
199 lang_hash (const void *key)
200 {
201   const unsigned char *p = key;
202   unsigned int h = 0;
203   while (canon_map[*p])
204     {
205       h = (h << 5) - h + canon_map[*p];
206       p++;
207     }
208 
209   return h;
210 }
211 #endif
212 
213 
214 struct hb_language_item_t {
215 
216   struct hb_language_item_t *next;
217   hb_language_t lang;
218 
operator ==hb_language_item_t219   inline bool operator == (const char *s) const {
220     return lang_equal (lang, s);
221   }
222 
operator =hb_language_item_t223   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++)
226       *p = canon_map[*p];
227 
228     return *this;
229   }
230 
finishhb_language_item_t231   void finish (void) { free ((void *) lang); }
232 };
233 
234 
235 /* Thread-safe lock-free language list */
236 
237 static hb_language_item_t *langs;
238 
239 static inline
free_langs(void)240 void free_langs (void)
241 {
242   while (langs) {
243     hb_language_item_t *next = langs->next;
244     langs->finish ();
245     free (langs);
246     langs = next;
247   }
248 }
249 
250 static hb_language_item_t *
lang_find_or_insert(const char * key)251 lang_find_or_insert (const char *key)
252 {
253 retry:
254   hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
255 
256   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
257     if (*lang == key)
258       return lang;
259 
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))
263     return NULL;
264   lang->next = first_lang;
265   *lang = key;
266 
267   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
268     free (lang);
269     goto retry;
270   }
271 
272 #ifdef HAVE_ATEXIT
273   if (!first_lang)
274     atexit (free_langs); /* First person registers atexit() callback. */
275 #endif
276 
277   return lang;
278 }
279 
280 
281 /**
282  * hb_language_from_string:
283  * @str: (array length=len):
284  * @len:
285  *
286  *
287  *
288  * Return value:
289  *
290  * Since: 1.0
291  **/
292 hb_language_t
hb_language_from_string(const char * str,int len)293 hb_language_from_string (const char *str, int len)
294 {
295   char strbuf[64];
296 
297   if (!str || !len || !*str)
298     return HB_LANGUAGE_INVALID;
299 
300   if (len >= 0)
301   {
302     len = MIN (len, (int) sizeof (strbuf) - 1);
303     str = (char *) memcpy (strbuf, str, len);
304     strbuf[len] = '\0';
305   }
306 
307   hb_language_item_t *item = lang_find_or_insert (str);
308 
309   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
310 }
311 
312 /**
313  * hb_language_to_string:
314  * @language:
315  *
316  *
317  *
318  * Return value: (transfer none):
319  *
320  * Since: 1.0
321  **/
322 const char *
hb_language_to_string(hb_language_t language)323 hb_language_to_string (hb_language_t language)
324 {
325   /* This is actually NULL-safe! */
326   return language->s;
327 }
328 
329 /**
330  * hb_language_get_default:
331  *
332  *
333  *
334  * Return value:
335  *
336  * Since: 1.0
337  **/
338 hb_language_t
hb_language_get_default(void)339 hb_language_get_default (void)
340 {
341   static hb_language_t default_language = HB_LANGUAGE_INVALID;
342 
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);
347   }
348 
349   return default_language;
350 }
351 
352 
353 /* hb_script_t */
354 
355 /**
356  * hb_script_from_iso15924_tag:
357  * @tag:
358  *
359  *
360  *
361  * Return value:
362  *
363  * Since: 1.0
364  **/
365 hb_script_t
hb_script_from_iso15924_tag(hb_tag_t tag)366 hb_script_from_iso15924_tag (hb_tag_t tag)
367 {
368   if (unlikely (tag == HB_TAG_NONE))
369     return HB_SCRIPT_INVALID;
370 
371   /* Be lenient, adjust case (one capital letter followed by three small letters) */
372   tag = (tag & 0xDFDFDFDF) | 0x00202020;
373 
374   switch (tag) {
375 
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;
381 
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;
389   }
390 
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;
394 
395   /* Otherwise, return unknown */
396   return HB_SCRIPT_UNKNOWN;
397 }
398 
399 /**
400  * hb_script_from_string:
401  * @s: (array length=len):
402  * @len:
403  *
404  *
405  *
406  * Return value:
407  *
408  * Since: 1.0
409  **/
410 hb_script_t
hb_script_from_string(const char * s,int len)411 hb_script_from_string (const char *s, int len)
412 {
413   return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
414 }
415 
416 /**
417  * hb_script_to_iso15924_tag:
418  * @script:
419  *
420  *
421  *
422  * Return value:
423  *
424  * Since: 1.0
425  **/
426 hb_tag_t
hb_script_to_iso15924_tag(hb_script_t script)427 hb_script_to_iso15924_tag (hb_script_t script)
428 {
429   return (hb_tag_t) script;
430 }
431 
432 /**
433  * hb_script_get_horizontal_direction:
434  * @script:
435  *
436  *
437  *
438  * Return value:
439  *
440  * Since: 1.0
441  **/
442 hb_direction_t
hb_script_get_horizontal_direction(hb_script_t script)443 hb_script_get_horizontal_direction (hb_script_t script)
444 {
445   /* http://goo.gl/x9ilM */
446   switch ((hb_tag_t) script)
447   {
448     /* Unicode-1.1 additions */
449     case HB_SCRIPT_ARABIC:
450     case HB_SCRIPT_HEBREW:
451 
452     /* Unicode-3.0 additions */
453     case HB_SCRIPT_SYRIAC:
454     case HB_SCRIPT_THAANA:
455 
456     /* Unicode-4.0 additions */
457     case HB_SCRIPT_CYPRIOT:
458 
459     /* Unicode-4.1 additions */
460     case HB_SCRIPT_KHAROSHTHI:
461 
462     /* Unicode-5.0 additions */
463     case HB_SCRIPT_PHOENICIAN:
464     case HB_SCRIPT_NKO:
465 
466     /* Unicode-5.1 additions */
467     case HB_SCRIPT_LYDIAN:
468 
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:
477 
478     /* Unicode-6.0 additions */
479     case HB_SCRIPT_MANDAIC:
480 
481     /* Unicode-6.1 additions */
482     case HB_SCRIPT_MEROITIC_CURSIVE:
483     case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
484 
485       return HB_DIRECTION_RTL;
486   }
487 
488   return HB_DIRECTION_LTR;
489 }
490 
491 
492 /* hb_user_data_array_t */
493 
494 bool
set(hb_user_data_key_t * key,void * data,hb_destroy_func_t destroy,hb_bool_t replace)495 hb_user_data_array_t::set (hb_user_data_key_t *key,
496 			   void *              data,
497 			   hb_destroy_func_t   destroy,
498 			   hb_bool_t           replace)
499 {
500   if (!key)
501     return false;
502 
503   if (replace) {
504     if (!data && !destroy) {
505       items.remove (key, lock);
506       return true;
507     }
508   }
509   hb_user_data_item_t item = {key, data, destroy};
510   bool ret = !!items.replace_or_insert (item, lock, replace);
511 
512   return ret;
513 }
514 
515 void *
get(hb_user_data_key_t * key)516 hb_user_data_array_t::get (hb_user_data_key_t *key)
517 {
518   hb_user_data_item_t item = {NULL };
519 
520   return items.find (key, &item, lock) ? item.data : NULL;
521 }
522 
523 
524 /* hb_version */
525 
526 /**
527  * hb_version:
528  * @major: (out): Library major version component.
529  * @minor: (out): Library minor version component.
530  * @micro: (out): Library micro version component.
531  *
532  * Returns library version as three integer components.
533  *
534  * Since: 1.0
535  **/
536 void
hb_version(unsigned int * major,unsigned int * minor,unsigned int * micro)537 hb_version (unsigned int *major,
538 	    unsigned int *minor,
539 	    unsigned int *micro)
540 {
541   *major = HB_VERSION_MAJOR;
542   *minor = HB_VERSION_MINOR;
543   *micro = HB_VERSION_MICRO;
544 }
545 
546 /**
547  * hb_version_string:
548  *
549  * Returns library version as a string with three components.
550  *
551  * Return value: library version string.
552  *
553  * Since: 1.0
554  **/
555 const char *
hb_version_string(void)556 hb_version_string (void)
557 {
558   return HB_VERSION_STRING;
559 }
560 
561 /**
562  * hb_version_check:
563  * @major:
564  * @minor:
565  * @micro:
566  *
567  *
568  *
569  * Return value:
570  *
571  * Since: 1.0
572  **/
573 hb_bool_t
hb_version_check(unsigned int major,unsigned int minor,unsigned int micro)574 hb_version_check (unsigned int major,
575 		  unsigned int minor,
576 		  unsigned int micro)
577 {
578   return HB_VERSION_CHECK (major, minor, micro);
579 }
580