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