gfx/harfbuzz/src/hb-common.cc

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:8f5755c88543
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
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
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
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
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 *
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
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
217 inline bool operator == (const char *s) const {
218 return lang_equal (lang, s);
219 }
220
221 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
229 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 static inline
238 void free_langs (void)
239 {
240 while (langs) {
241 hb_language_item_t *next = langs->next;
242 langs->finish ();
243 free (langs);
244 langs = next;
245 }
246 }
247
248 static hb_language_item_t *
249 lang_find_or_insert (const char *key)
250 {
251 retry:
252 hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
253
254 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
255 if (*lang == key)
256 return lang;
257
258 /* Not found; allocate one. */
259 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
260 if (unlikely (!lang))
261 return NULL;
262 lang->next = first_lang;
263 *lang = key;
264
265 if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
266 free (lang);
267 goto retry;
268 }
269
270 #ifdef HAVE_ATEXIT
271 if (!first_lang)
272 atexit (free_langs); /* First person registers atexit() callback. */
273 #endif
274
275 return lang;
276 }
277
278
279 /**
280 * hb_language_from_string:
281 * @str: (array length=len):
282 * @len:
283 *
284 *
285 *
286 * Return value:
287 *
288 * Since: 1.0
289 **/
290 hb_language_t
291 hb_language_from_string (const char *str, int len)
292 {
293 char strbuf[64];
294
295 if (!str || !len || !*str)
296 return HB_LANGUAGE_INVALID;
297
298 if (len >= 0)
299 {
300 len = MIN (len, (int) sizeof (strbuf) - 1);
301 str = (char *) memcpy (strbuf, str, len);
302 strbuf[len] = '\0';
303 }
304
305 hb_language_item_t *item = lang_find_or_insert (str);
306
307 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
308 }
309
310 /**
311 * hb_language_to_string:
312 * @language:
313 *
314 *
315 *
316 * Return value: (transfer none):
317 *
318 * Since: 1.0
319 **/
320 const char *
321 hb_language_to_string (hb_language_t language)
322 {
323 /* This is actually NULL-safe! */
324 return language->s;
325 }
326
327 /**
328 * hb_language_get_default:
329 *
330 *
331 *
332 * Return value:
333 *
334 * Since: 1.0
335 **/
336 hb_language_t
337 hb_language_get_default (void)
338 {
339 static hb_language_t default_language = HB_LANGUAGE_INVALID;
340
341 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
342 if (unlikely (language == HB_LANGUAGE_INVALID)) {
343 language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
344 hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
345 }
346
347 return default_language;
348 }
349
350
351 /* hb_script_t */
352
353 /**
354 * hb_script_from_iso15924_tag:
355 * @tag:
356 *
357 *
358 *
359 * Return value:
360 *
361 * Since: 1.0
362 **/
363 hb_script_t
364 hb_script_from_iso15924_tag (hb_tag_t tag)
365 {
366 if (unlikely (tag == HB_TAG_NONE))
367 return HB_SCRIPT_INVALID;
368
369 /* Be lenient, adjust case (one capital letter followed by three small letters) */
370 tag = (tag & 0xDFDFDFDF) | 0x00202020;
371
372 switch (tag) {
373
374 /* These graduated from the 'Q' private-area codes, but
375 * the old code is still aliased by Unicode, and the Qaai
376 * one in use by ICU. */
377 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
378 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
379
380 /* Script variants from http://unicode.org/iso15924/ */
381 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
382 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
383 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
384 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
385 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
386 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
387 }
388
389 /* If it looks right, just use the tag as a script */
390 if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
391 return (hb_script_t) tag;
392
393 /* Otherwise, return unknown */
394 return HB_SCRIPT_UNKNOWN;
395 }
396
397 /**
398 * hb_script_from_string:
399 * @s: (array length=len):
400 * @len:
401 *
402 *
403 *
404 * Return value:
405 *
406 * Since: 1.0
407 **/
408 hb_script_t
409 hb_script_from_string (const char *s, int len)
410 {
411 return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
412 }
413
414 /**
415 * hb_script_to_iso15924_tag:
416 * @script:
417 *
418 *
419 *
420 * Return value:
421 *
422 * Since: 1.0
423 **/
424 hb_tag_t
425 hb_script_to_iso15924_tag (hb_script_t script)
426 {
427 return (hb_tag_t) script;
428 }
429
430 /**
431 * hb_script_get_horizontal_direction:
432 * @script:
433 *
434 *
435 *
436 * Return value:
437 *
438 * Since: 1.0
439 **/
440 hb_direction_t
441 hb_script_get_horizontal_direction (hb_script_t script)
442 {
443 /* http://goo.gl/x9ilM */
444 switch ((hb_tag_t) script)
445 {
446 /* Unicode-1.1 additions */
447 case HB_SCRIPT_ARABIC:
448 case HB_SCRIPT_HEBREW:
449
450 /* Unicode-3.0 additions */
451 case HB_SCRIPT_SYRIAC:
452 case HB_SCRIPT_THAANA:
453
454 /* Unicode-4.0 additions */
455 case HB_SCRIPT_CYPRIOT:
456
457 /* Unicode-4.1 additions */
458 case HB_SCRIPT_KHAROSHTHI:
459
460 /* Unicode-5.0 additions */
461 case HB_SCRIPT_PHOENICIAN:
462 case HB_SCRIPT_NKO:
463
464 /* Unicode-5.1 additions */
465 case HB_SCRIPT_LYDIAN:
466
467 /* Unicode-5.2 additions */
468 case HB_SCRIPT_AVESTAN:
469 case HB_SCRIPT_IMPERIAL_ARAMAIC:
470 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
471 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
472 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
473 case HB_SCRIPT_OLD_TURKIC:
474 case HB_SCRIPT_SAMARITAN:
475
476 /* Unicode-6.0 additions */
477 case HB_SCRIPT_MANDAIC:
478
479 /* Unicode-6.1 additions */
480 case HB_SCRIPT_MEROITIC_CURSIVE:
481 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
482
483 return HB_DIRECTION_RTL;
484 }
485
486 return HB_DIRECTION_LTR;
487 }
488
489
490 /* hb_user_data_array_t */
491
492 bool
493 hb_user_data_array_t::set (hb_user_data_key_t *key,
494 void * data,
495 hb_destroy_func_t destroy,
496 hb_bool_t replace)
497 {
498 if (!key)
499 return false;
500
501 if (replace) {
502 if (!data && !destroy) {
503 items.remove (key, lock);
504 return true;
505 }
506 }
507 hb_user_data_item_t item = {key, data, destroy};
508 bool ret = !!items.replace_or_insert (item, lock, replace);
509
510 return ret;
511 }
512
513 void *
514 hb_user_data_array_t::get (hb_user_data_key_t *key)
515 {
516 hb_user_data_item_t item = {NULL };
517
518 return items.find (key, &item, lock) ? item.data : NULL;
519 }
520
521
522 /* hb_version */
523
524 /**
525 * hb_version:
526 * @major: (out): Library major version component.
527 * @minor: (out): Library minor version component.
528 * @micro: (out): Library micro version component.
529 *
530 * Returns library version as three integer components.
531 *
532 * Since: 1.0
533 **/
534 void
535 hb_version (unsigned int *major,
536 unsigned int *minor,
537 unsigned int *micro)
538 {
539 *major = HB_VERSION_MAJOR;
540 *minor = HB_VERSION_MINOR;
541 *micro = HB_VERSION_MICRO;
542 }
543
544 /**
545 * hb_version_string:
546 *
547 * Returns library version as a string with three components.
548 *
549 * Return value: library version string.
550 *
551 * Since: 1.0
552 **/
553 const char *
554 hb_version_string (void)
555 {
556 return HB_VERSION_STRING;
557 }
558
559 /**
560 * hb_version_check:
561 * @major:
562 * @minor:
563 * @micro:
564 *
565 *
566 *
567 * Return value:
568 *
569 * Since: 1.0
570 **/
571 hb_bool_t
572 hb_version_check (unsigned int major,
573 unsigned int minor,
574 unsigned int micro)
575 {
576 return HB_VERSION_CHECK (major, minor, micro);
577 }

mercurial