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-mutex-private.hh"
32 #include "hb-object-private.hh"
33
34 #include <locale.h>
35
36
37 /* hb_options_t */
38
39 hb_options_union_t _hb_options;
40
41 void
_hb_options_init(void)42 _hb_options_init (void)
43 {
44 hb_options_union_t u;
45 u.i = 0;
46 u.opts.initialized = 1;
47
48 char *c = getenv ("HB_OPTIONS");
49 u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
50
51 /* This is idempotent and threadsafe. */
52 _hb_options = u;
53 }
54
55
56 /* hb_tag_t */
57
58 /**
59 * hb_tag_from_string:
60 * @str: (array length=len):
61 * @len:
62 *
63 *
64 *
65 * Return value:
66 *
67 * Since: 1.0
68 **/
69 hb_tag_t
hb_tag_from_string(const char * str,int len)70 hb_tag_from_string (const char *str, int len)
71 {
72 char tag[4];
73 unsigned int i;
74
75 if (!str || !len || !*str)
76 return HB_TAG_NONE;
77
78 if (len < 0 || len > 4)
79 len = 4;
80 for (i = 0; i < (unsigned) len && str[i]; i++)
81 tag[i] = str[i];
82 for (; i < 4; i++)
83 tag[i] = ' ';
84
85 return HB_TAG_CHAR4 (tag);
86 }
87
88 /**
89 * hb_tag_to_string:
90 * @tag:
91 * @buf: (array fixed-size=4):
92 *
93 *
94 *
95 * Since: 1.0
96 **/
97 void
hb_tag_to_string(hb_tag_t tag,char * buf)98 hb_tag_to_string (hb_tag_t tag, char *buf)
99 {
100 buf[0] = (char) (uint8_t) (tag >> 24);
101 buf[1] = (char) (uint8_t) (tag >> 16);
102 buf[2] = (char) (uint8_t) (tag >> 8);
103 buf[3] = (char) (uint8_t) (tag >> 0);
104 }
105
106
107 /* hb_direction_t */
108
109 const char direction_strings[][4] = {
110 "ltr",
111 "rtl",
112 "ttb",
113 "btt"
114 };
115
116 /**
117 * hb_direction_from_string:
118 * @str: (array length=len):
119 * @len:
120 *
121 *
122 *
123 * Return value:
124 *
125 * Since: 1.0
126 **/
127 hb_direction_t
hb_direction_from_string(const char * str,int len)128 hb_direction_from_string (const char *str, int len)
129 {
130 if (unlikely (!str || !len || !*str))
131 return HB_DIRECTION_INVALID;
132
133 /* Lets match loosely: just match the first letter, such that
134 * all of "ltr", "left-to-right", etc work!
135 */
136 char c = TOLOWER (str[0]);
137 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
138 if (c == direction_strings[i][0])
139 return (hb_direction_t) (HB_DIRECTION_LTR + i);
140
141 return HB_DIRECTION_INVALID;
142 }
143
144 /**
145 * hb_direction_to_string:
146 * @direction:
147 *
148 *
149 *
150 * Return value: (transfer none):
151 *
152 * Since: 1.0
153 **/
154 const char *
hb_direction_to_string(hb_direction_t direction)155 hb_direction_to_string (hb_direction_t direction)
156 {
157 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
158 < ARRAY_LENGTH (direction_strings)))
159 return direction_strings[direction - HB_DIRECTION_LTR];
160
161 return "invalid";
162 }
163
164
165 /* hb_language_t */
166
167 struct hb_language_impl_t {
168 const char s[1];
169 };
170
171 static const char canon_map[256] = {
172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
175 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
176 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
177 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
178 0, '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, 0
180 };
181
182 static hb_bool_t
lang_equal(hb_language_t v1,const void * v2)183 lang_equal (hb_language_t v1,
184 const void *v2)
185 {
186 const unsigned char *p1 = (const unsigned char *) v1;
187 const unsigned char *p2 = (const unsigned char *) v2;
188
189 while (*p1 && *p1 == canon_map[*p2])
190 p1++, p2++;
191
192 return *p1 == canon_map[*p2];
193 }
194
195 #if 0
196 static unsigned int
197 lang_hash (const void *key)
198 {
199 const unsigned char *p = key;
200 unsigned int h = 0;
201 while (canon_map[*p])
202 {
203 h = (h << 5) - h + canon_map[*p];
204 p++;
205 }
206
207 return h;
208 }
209 #endif
210
211
212 struct hb_language_item_t {
213
214 struct hb_language_item_t *next;
215 hb_language_t lang;
216
operator ==hb_language_item_t217 inline bool operator == (const char *s) const {
218 return lang_equal (lang, s);
219 }
220
operator =hb_language_item_t221 inline hb_language_item_t & operator = (const char *s) {
222 lang = (hb_language_t) strdup (s);
223 for (unsigned char *p = (unsigned char *) lang; *p; p++)
224 *p = canon_map[*p];
225
226 return *this;
227 }
228
finishhb_language_item_t229 void finish (void) { free ((void *) lang); }
230 };
231
232
233 /* Thread-safe lock-free language list */
234
235 static hb_language_item_t *langs;
236
237 #ifdef HB_USE_ATEXIT
238 static
free_langs(void)239 void free_langs (void)
240 {
241 while (langs) {
242 hb_language_item_t *next = langs->next;
243 langs->finish ();
244 free (langs);
245 langs = next;
246 }
247 }
248 #endif
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 HB_USE_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 /* NUL-terminate it. */
303 len = MIN (len, (int) sizeof (strbuf) - 1);
304 memcpy (strbuf, str, len);
305 strbuf[len] = '\0';
306 str = strbuf;
307 }
308
309 hb_language_item_t *item = lang_find_or_insert (str);
310
311 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
312 }
313
314 /**
315 * hb_language_to_string:
316 * @language:
317 *
318 *
319 *
320 * Return value: (transfer none):
321 *
322 * Since: 1.0
323 **/
324 const char *
hb_language_to_string(hb_language_t language)325 hb_language_to_string (hb_language_t language)
326 {
327 /* This is actually NULL-safe! */
328 return language->s;
329 }
330
331 /**
332 * hb_language_get_default:
333 *
334 *
335 *
336 * Return value:
337 *
338 * Since: 1.0
339 **/
340 hb_language_t
hb_language_get_default(void)341 hb_language_get_default (void)
342 {
343 static hb_language_t default_language = HB_LANGUAGE_INVALID;
344
345 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
346 if (unlikely (language == HB_LANGUAGE_INVALID)) {
347 language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
348 (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
349 }
350
351 return default_language;
352 }
353
354
355 /* hb_script_t */
356
357 /**
358 * hb_script_from_iso15924_tag:
359 * @tag:
360 *
361 *
362 *
363 * Return value:
364 *
365 * Since: 1.0
366 **/
367 hb_script_t
hb_script_from_iso15924_tag(hb_tag_t tag)368 hb_script_from_iso15924_tag (hb_tag_t tag)
369 {
370 if (unlikely (tag == HB_TAG_NONE))
371 return HB_SCRIPT_INVALID;
372
373 /* Be lenient, adjust case (one capital letter followed by three small letters) */
374 tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
375
376 switch (tag) {
377
378 /* These graduated from the 'Q' private-area codes, but
379 * the old code is still aliased by Unicode, and the Qaai
380 * one in use by ICU. */
381 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
382 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
383
384 /* Script variants from http://unicode.org/iso15924/ */
385 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
386 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
387 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
388 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
389 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
390 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
391 }
392
393 /* If it looks right, just use the tag as a script */
394 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
395 return (hb_script_t) tag;
396
397 /* Otherwise, return unknown */
398 return HB_SCRIPT_UNKNOWN;
399 }
400
401 /**
402 * hb_script_from_string:
403 * @s: (array length=len):
404 * @len:
405 *
406 *
407 *
408 * Return value:
409 *
410 * Since: 1.0
411 **/
412 hb_script_t
hb_script_from_string(const char * s,int len)413 hb_script_from_string (const char *s, int len)
414 {
415 return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
416 }
417
418 /**
419 * hb_script_to_iso15924_tag:
420 * @script:
421 *
422 *
423 *
424 * Return value:
425 *
426 * Since: 1.0
427 **/
428 hb_tag_t
hb_script_to_iso15924_tag(hb_script_t script)429 hb_script_to_iso15924_tag (hb_script_t script)
430 {
431 return (hb_tag_t) script;
432 }
433
434 /**
435 * hb_script_get_horizontal_direction:
436 * @script:
437 *
438 *
439 *
440 * Return value:
441 *
442 * Since: 1.0
443 **/
444 hb_direction_t
hb_script_get_horizontal_direction(hb_script_t script)445 hb_script_get_horizontal_direction (hb_script_t script)
446 {
447 /* http://goo.gl/x9ilM */
448 switch ((hb_tag_t) script)
449 {
450 /* Unicode-1.1 additions */
451 case HB_SCRIPT_ARABIC:
452 case HB_SCRIPT_HEBREW:
453
454 /* Unicode-3.0 additions */
455 case HB_SCRIPT_SYRIAC:
456 case HB_SCRIPT_THAANA:
457
458 /* Unicode-4.0 additions */
459 case HB_SCRIPT_CYPRIOT:
460
461 /* Unicode-4.1 additions */
462 case HB_SCRIPT_KHAROSHTHI:
463
464 /* Unicode-5.0 additions */
465 case HB_SCRIPT_PHOENICIAN:
466 case HB_SCRIPT_NKO:
467
468 /* Unicode-5.1 additions */
469 case HB_SCRIPT_LYDIAN:
470
471 /* Unicode-5.2 additions */
472 case HB_SCRIPT_AVESTAN:
473 case HB_SCRIPT_IMPERIAL_ARAMAIC:
474 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
475 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
476 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
477 case HB_SCRIPT_OLD_TURKIC:
478 case HB_SCRIPT_SAMARITAN:
479
480 /* Unicode-6.0 additions */
481 case HB_SCRIPT_MANDAIC:
482
483 /* Unicode-6.1 additions */
484 case HB_SCRIPT_MEROITIC_CURSIVE:
485 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
486
487 /* Unicode-7.0 additions */
488 case HB_SCRIPT_MANICHAEAN:
489 case HB_SCRIPT_MENDE_KIKAKUI:
490 case HB_SCRIPT_NABATAEAN:
491 case HB_SCRIPT_OLD_NORTH_ARABIAN:
492 case HB_SCRIPT_PALMYRENE:
493 case HB_SCRIPT_PSALTER_PAHLAVI:
494
495 return HB_DIRECTION_RTL;
496 }
497
498 return HB_DIRECTION_LTR;
499 }
500
501
502 /* hb_user_data_array_t */
503
504 bool
set(hb_user_data_key_t * key,void * data,hb_destroy_func_t destroy,hb_bool_t replace)505 hb_user_data_array_t::set (hb_user_data_key_t *key,
506 void * data,
507 hb_destroy_func_t destroy,
508 hb_bool_t replace)
509 {
510 if (!key)
511 return false;
512
513 if (replace) {
514 if (!data && !destroy) {
515 items.remove (key, lock);
516 return true;
517 }
518 }
519 hb_user_data_item_t item = {key, data, destroy};
520 bool ret = !!items.replace_or_insert (item, lock, replace);
521
522 return ret;
523 }
524
525 void *
get(hb_user_data_key_t * key)526 hb_user_data_array_t::get (hb_user_data_key_t *key)
527 {
528 hb_user_data_item_t item = {NULL };
529
530 return items.find (key, &item, lock) ? item.data : NULL;
531 }
532
533
534 /* hb_version */
535
536 /**
537 * hb_version:
538 * @major: (out): Library major version component.
539 * @minor: (out): Library minor version component.
540 * @micro: (out): Library micro version component.
541 *
542 * Returns library version as three integer components.
543 *
544 * Since: 1.0
545 **/
546 void
hb_version(unsigned int * major,unsigned int * minor,unsigned int * micro)547 hb_version (unsigned int *major,
548 unsigned int *minor,
549 unsigned int *micro)
550 {
551 *major = HB_VERSION_MAJOR;
552 *minor = HB_VERSION_MINOR;
553 *micro = HB_VERSION_MICRO;
554 }
555
556 /**
557 * hb_version_string:
558 *
559 * Returns library version as a string with three components.
560 *
561 * Return value: library version string.
562 *
563 * Since: 1.0
564 **/
565 const char *
hb_version_string(void)566 hb_version_string (void)
567 {
568 return HB_VERSION_STRING;
569 }
570
571 /**
572 * hb_version_atleast:
573 * @major:
574 * @minor:
575 * @micro:
576 *
577 *
578 *
579 * Return value:
580 *
581 * Since: 1.0
582 **/
583 hb_bool_t
hb_version_atleast(unsigned int major,unsigned int minor,unsigned int micro)584 hb_version_atleast (unsigned int major,
585 unsigned int minor,
586 unsigned int micro)
587 {
588 return HB_VERSION_ATLEAST (major, minor, micro);
589 }
590