|
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 } |