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.hh"
30
31 #include "hb-machinery.hh"
32
33 #include <locale.h>
34 #ifdef HAVE_XLOCALE_H
35 #include <xlocale.h>
36 #endif
37
38
39 /**
40 * SECTION:hb-common
41 * @title: hb-common
42 * @short_description: Common data types
43 * @include: hb.h
44 *
45 * Common data types used across HarfBuzz are defined here.
46 **/
47
48
49 /* hb_options_t */
50
51 hb_atomic_int_t _hb_options;
52
53 void
_hb_options_init()54 _hb_options_init ()
55 {
56 hb_options_union_t u;
57 u.i = 0;
58 u.opts.initialized = true;
59
60 const char *c = getenv ("HB_OPTIONS");
61 if (c)
62 {
63 while (*c)
64 {
65 const char *p = strchr (c, ':');
66 if (!p)
67 p = c + strlen (c);
68
69 #define OPTION(name, symbol) \
70 if (0 == strncmp (c, name, p - c) && strlen (name) == p - c) do { u.opts.symbol = true; } while (0)
71
72 OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
73 OPTION ("aat", aat);
74
75 #undef OPTION
76
77 c = *p ? p + 1 : p;
78 }
79
80 }
81
82 /* This is idempotent and threadsafe. */
83 _hb_options.set_relaxed (u.i);
84 }
85
86
87 /* hb_tag_t */
88
89 /**
90 * hb_tag_from_string:
91 * @str: (array length=len) (element-type uint8_t):
92 * @len:
93 *
94 *
95 *
96 * Return value:
97 *
98 * Since: 0.9.2
99 **/
100 hb_tag_t
hb_tag_from_string(const char * str,int len)101 hb_tag_from_string (const char *str, int len)
102 {
103 char tag[4];
104 unsigned int i;
105
106 if (!str || !len || !*str)
107 return HB_TAG_NONE;
108
109 if (len < 0 || len > 4)
110 len = 4;
111 for (i = 0; i < (unsigned) len && str[i]; i++)
112 tag[i] = str[i];
113 for (; i < 4; i++)
114 tag[i] = ' ';
115
116 return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
117 }
118
119 /**
120 * hb_tag_to_string:
121 * @tag:
122 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t):
123 *
124 *
125 *
126 * Since: 0.9.5
127 **/
128 void
hb_tag_to_string(hb_tag_t tag,char * buf)129 hb_tag_to_string (hb_tag_t tag, char *buf)
130 {
131 buf[0] = (char) (uint8_t) (tag >> 24);
132 buf[1] = (char) (uint8_t) (tag >> 16);
133 buf[2] = (char) (uint8_t) (tag >> 8);
134 buf[3] = (char) (uint8_t) (tag >> 0);
135 }
136
137
138 /* hb_direction_t */
139
140 const char direction_strings[][4] = {
141 "ltr",
142 "rtl",
143 "ttb",
144 "btt"
145 };
146
147 /**
148 * hb_direction_from_string:
149 * @str: (array length=len) (element-type uint8_t):
150 * @len:
151 *
152 *
153 *
154 * Return value:
155 *
156 * Since: 0.9.2
157 **/
158 hb_direction_t
hb_direction_from_string(const char * str,int len)159 hb_direction_from_string (const char *str, int len)
160 {
161 if (unlikely (!str || !len || !*str))
162 return HB_DIRECTION_INVALID;
163
164 /* Lets match loosely: just match the first letter, such that
165 * all of "ltr", "left-to-right", etc work!
166 */
167 char c = TOLOWER (str[0]);
168 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
169 if (c == direction_strings[i][0])
170 return (hb_direction_t) (HB_DIRECTION_LTR + i);
171
172 return HB_DIRECTION_INVALID;
173 }
174
175 /**
176 * hb_direction_to_string:
177 * @direction:
178 *
179 *
180 *
181 * Return value: (transfer none):
182 *
183 * Since: 0.9.2
184 **/
185 const char *
hb_direction_to_string(hb_direction_t direction)186 hb_direction_to_string (hb_direction_t direction)
187 {
188 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
189 < ARRAY_LENGTH (direction_strings)))
190 return direction_strings[direction - HB_DIRECTION_LTR];
191
192 return "invalid";
193 }
194
195
196 /* hb_language_t */
197
198 struct hb_language_impl_t {
199 const char s[1];
200 };
201
202 static const char canon_map[256] = {
203 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
204 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
205 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
206 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
207 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
208 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
209 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
210 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
211 };
212
213 static bool
lang_equal(hb_language_t v1,const void * v2)214 lang_equal (hb_language_t v1,
215 const void *v2)
216 {
217 const unsigned char *p1 = (const unsigned char *) v1;
218 const unsigned char *p2 = (const unsigned char *) v2;
219
220 while (*p1 && *p1 == canon_map[*p2]) {
221 p1++;
222 p2++;
223 }
224
225 return *p1 == canon_map[*p2];
226 }
227
228 #if 0
229 static unsigned int
230 lang_hash (const void *key)
231 {
232 const unsigned char *p = key;
233 unsigned int h = 0;
234 while (canon_map[*p])
235 {
236 h = (h << 5) - h + canon_map[*p];
237 p++;
238 }
239
240 return h;
241 }
242 #endif
243
244
245 struct hb_language_item_t {
246
247 struct hb_language_item_t *next;
248 hb_language_t lang;
249
operator ==hb_language_item_t250 bool operator == (const char *s) const
251 { return lang_equal (lang, s); }
252
operator =hb_language_item_t253 hb_language_item_t & operator = (const char *s) {
254 /* If a custom allocated is used calling strdup() pairs
255 badly with a call to the custom free() in fini() below.
256 Therefore don't call strdup(), implement its behavior.
257 */
258 size_t len = strlen(s) + 1;
259 lang = (hb_language_t) malloc(len);
260 if (likely (lang))
261 {
262 memcpy((unsigned char *) lang, s, len);
263 for (unsigned char *p = (unsigned char *) lang; *p; p++)
264 *p = canon_map[*p];
265 }
266
267 return *this;
268 }
269
finihb_language_item_t270 void fini () { free ((void *) lang); }
271 };
272
273
274 /* Thread-safe lock-free language list */
275
276 static hb_atomic_ptr_t <hb_language_item_t> langs;
277
278 #if HB_USE_ATEXIT
279 static void
free_langs()280 free_langs ()
281 {
282 retry:
283 hb_language_item_t *first_lang = langs;
284 if (unlikely (!langs.cmpexch (first_lang, nullptr)))
285 goto retry;
286
287 while (first_lang) {
288 hb_language_item_t *next = first_lang->next;
289 first_lang->fini ();
290 free (first_lang);
291 first_lang = next;
292 }
293 }
294 #endif
295
296 static hb_language_item_t *
lang_find_or_insert(const char * key)297 lang_find_or_insert (const char *key)
298 {
299 retry:
300 hb_language_item_t *first_lang = langs;
301
302 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
303 if (*lang == key)
304 return lang;
305
306 /* Not found; allocate one. */
307 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
308 if (unlikely (!lang))
309 return nullptr;
310 lang->next = first_lang;
311 *lang = key;
312 if (unlikely (!lang->lang))
313 {
314 free (lang);
315 return nullptr;
316 }
317
318 if (unlikely (!langs.cmpexch (first_lang, lang)))
319 {
320 lang->fini ();
321 free (lang);
322 goto retry;
323 }
324
325 #if HB_USE_ATEXIT
326 if (!first_lang)
327 atexit (free_langs); /* First person registers atexit() callback. */
328 #endif
329
330 return lang;
331 }
332
333
334 /**
335 * hb_language_from_string:
336 * @str: (array length=len) (element-type uint8_t): a string representing
337 * a BCP 47 language tag
338 * @len: length of the @str, or -1 if it is %NULL-terminated.
339 *
340 * Converts @str representing a BCP 47 language tag to the corresponding
341 * #hb_language_t.
342 *
343 * Return value: (transfer none):
344 * The #hb_language_t corresponding to the BCP 47 language tag.
345 *
346 * Since: 0.9.2
347 **/
348 hb_language_t
hb_language_from_string(const char * str,int len)349 hb_language_from_string (const char *str, int len)
350 {
351 if (!str || !len || !*str)
352 return HB_LANGUAGE_INVALID;
353
354 hb_language_item_t *item = nullptr;
355 if (len >= 0)
356 {
357 /* NUL-terminate it. */
358 char strbuf[64];
359 len = hb_min (len, (int) sizeof (strbuf) - 1);
360 memcpy (strbuf, str, len);
361 strbuf[len] = '\0';
362 item = lang_find_or_insert (strbuf);
363 }
364 else
365 item = lang_find_or_insert (str);
366
367 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
368 }
369
370 /**
371 * hb_language_to_string:
372 * @language: an #hb_language_t to convert.
373 *
374 * See hb_language_from_string().
375 *
376 * Return value: (transfer none):
377 * A %NULL-terminated string representing the @language. Must not be freed by
378 * the caller.
379 *
380 * Since: 0.9.2
381 **/
382 const char *
hb_language_to_string(hb_language_t language)383 hb_language_to_string (hb_language_t language)
384 {
385 /* This is actually nullptr-safe! */
386 return language->s;
387 }
388
389 /**
390 * hb_language_get_default:
391 *
392 * Get default language from current locale.
393 *
394 * Note that the first time this function is called, it calls
395 * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying
396 * setlocale function is, in many implementations, NOT threadsafe. To avoid
397 * problems, call this function once before multiple threads can call it.
398 * This function is only used from hb_buffer_guess_segment_properties() by
399 * HarfBuzz itself.
400 *
401 * Return value: (transfer none):
402 *
403 * Since: 0.9.2
404 **/
405 hb_language_t
hb_language_get_default()406 hb_language_get_default ()
407 {
408 static hb_atomic_ptr_t <hb_language_t> default_language;
409
410 hb_language_t language = default_language;
411 if (unlikely (language == HB_LANGUAGE_INVALID))
412 {
413 language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
414 (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
415 }
416
417 return language;
418 }
419
420
421 /* hb_script_t */
422
423 /**
424 * hb_script_from_iso15924_tag:
425 * @tag: an #hb_tag_t representing an ISO 15924 tag.
426 *
427 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
428 *
429 * Return value:
430 * An #hb_script_t corresponding to the ISO 15924 tag.
431 *
432 * Since: 0.9.2
433 **/
434 hb_script_t
hb_script_from_iso15924_tag(hb_tag_t tag)435 hb_script_from_iso15924_tag (hb_tag_t tag)
436 {
437 if (unlikely (tag == HB_TAG_NONE))
438 return HB_SCRIPT_INVALID;
439
440 /* Be lenient, adjust case (one capital letter followed by three small letters) */
441 tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
442
443 switch (tag) {
444
445 /* These graduated from the 'Q' private-area codes, but
446 * the old code is still aliased by Unicode, and the Qaai
447 * one in use by ICU. */
448 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
449 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
450
451 /* Script variants from https://unicode.org/iso15924/ */
452 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
453 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
454 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
455 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
456 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
457 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
458 }
459
460 /* If it looks right, just use the tag as a script */
461 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
462 return (hb_script_t) tag;
463
464 /* Otherwise, return unknown */
465 return HB_SCRIPT_UNKNOWN;
466 }
467
468 /**
469 * hb_script_from_string:
470 * @str: (array length=len) (element-type uint8_t): a string representing an
471 * ISO 15924 tag.
472 * @len: length of the @str, or -1 if it is %NULL-terminated.
473 *
474 * Converts a string @str representing an ISO 15924 script tag to a
475 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
476 * hb_script_from_iso15924_tag().
477 *
478 * Return value:
479 * An #hb_script_t corresponding to the ISO 15924 tag.
480 *
481 * Since: 0.9.2
482 **/
483 hb_script_t
hb_script_from_string(const char * str,int len)484 hb_script_from_string (const char *str, int len)
485 {
486 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
487 }
488
489 /**
490 * hb_script_to_iso15924_tag:
491 * @script: an #hb_script_t to convert.
492 *
493 * See hb_script_from_iso15924_tag().
494 *
495 * Return value:
496 * An #hb_tag_t representing an ISO 15924 script tag.
497 *
498 * Since: 0.9.2
499 **/
500 hb_tag_t
hb_script_to_iso15924_tag(hb_script_t script)501 hb_script_to_iso15924_tag (hb_script_t script)
502 {
503 return (hb_tag_t) script;
504 }
505
506 /**
507 * hb_script_get_horizontal_direction:
508 * @script:
509 *
510 *
511 *
512 * Return value:
513 *
514 * Since: 0.9.2
515 **/
516 hb_direction_t
hb_script_get_horizontal_direction(hb_script_t script)517 hb_script_get_horizontal_direction (hb_script_t script)
518 {
519 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
520 switch ((hb_tag_t) script)
521 {
522 /* Unicode-1.1 additions */
523 case HB_SCRIPT_ARABIC:
524 case HB_SCRIPT_HEBREW:
525
526 /* Unicode-3.0 additions */
527 case HB_SCRIPT_SYRIAC:
528 case HB_SCRIPT_THAANA:
529
530 /* Unicode-4.0 additions */
531 case HB_SCRIPT_CYPRIOT:
532
533 /* Unicode-4.1 additions */
534 case HB_SCRIPT_KHAROSHTHI:
535
536 /* Unicode-5.0 additions */
537 case HB_SCRIPT_PHOENICIAN:
538 case HB_SCRIPT_NKO:
539
540 /* Unicode-5.1 additions */
541 case HB_SCRIPT_LYDIAN:
542
543 /* Unicode-5.2 additions */
544 case HB_SCRIPT_AVESTAN:
545 case HB_SCRIPT_IMPERIAL_ARAMAIC:
546 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
547 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
548 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
549 case HB_SCRIPT_OLD_TURKIC:
550 case HB_SCRIPT_SAMARITAN:
551
552 /* Unicode-6.0 additions */
553 case HB_SCRIPT_MANDAIC:
554
555 /* Unicode-6.1 additions */
556 case HB_SCRIPT_MEROITIC_CURSIVE:
557 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
558
559 /* Unicode-7.0 additions */
560 case HB_SCRIPT_MANICHAEAN:
561 case HB_SCRIPT_MENDE_KIKAKUI:
562 case HB_SCRIPT_NABATAEAN:
563 case HB_SCRIPT_OLD_NORTH_ARABIAN:
564 case HB_SCRIPT_PALMYRENE:
565 case HB_SCRIPT_PSALTER_PAHLAVI:
566
567 /* Unicode-8.0 additions */
568 case HB_SCRIPT_HATRAN:
569
570 /* Unicode-9.0 additions */
571 case HB_SCRIPT_ADLAM:
572
573 /* Unicode-11.0 additions */
574 case HB_SCRIPT_HANIFI_ROHINGYA:
575 case HB_SCRIPT_OLD_SOGDIAN:
576 case HB_SCRIPT_SOGDIAN:
577
578 return HB_DIRECTION_RTL;
579
580
581 /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
582 case HB_SCRIPT_OLD_HUNGARIAN:
583 case HB_SCRIPT_OLD_ITALIC:
584 case HB_SCRIPT_RUNIC:
585
586 return HB_DIRECTION_INVALID;
587 }
588
589 return HB_DIRECTION_LTR;
590 }
591
592
593 /* hb_user_data_array_t */
594
595 bool
set(hb_user_data_key_t * key,void * data,hb_destroy_func_t destroy,hb_bool_t replace)596 hb_user_data_array_t::set (hb_user_data_key_t *key,
597 void * data,
598 hb_destroy_func_t destroy,
599 hb_bool_t replace)
600 {
601 if (!key)
602 return false;
603
604 if (replace) {
605 if (!data && !destroy) {
606 items.remove (key, lock);
607 return true;
608 }
609 }
610 hb_user_data_item_t item = {key, data, destroy};
611 bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
612
613 return ret;
614 }
615
616 void *
get(hb_user_data_key_t * key)617 hb_user_data_array_t::get (hb_user_data_key_t *key)
618 {
619 hb_user_data_item_t item = {nullptr, nullptr, nullptr};
620
621 return items.find (key, &item, lock) ? item.data : nullptr;
622 }
623
624
625 /* hb_version */
626
627
628 /**
629 * SECTION:hb-version
630 * @title: hb-version
631 * @short_description: Information about the version of HarfBuzz in use
632 * @include: hb.h
633 *
634 * These functions and macros allow accessing version of the HarfBuzz
635 * library used at compile- as well as run-time, and to direct code
636 * conditionally based on those versions, again, at compile- or run-time.
637 **/
638
639
640 /**
641 * hb_version:
642 * @major: (out): Library major version component.
643 * @minor: (out): Library minor version component.
644 * @micro: (out): Library micro version component.
645 *
646 * Returns library version as three integer components.
647 *
648 * Since: 0.9.2
649 **/
650 void
hb_version(unsigned int * major,unsigned int * minor,unsigned int * micro)651 hb_version (unsigned int *major,
652 unsigned int *minor,
653 unsigned int *micro)
654 {
655 *major = HB_VERSION_MAJOR;
656 *minor = HB_VERSION_MINOR;
657 *micro = HB_VERSION_MICRO;
658 }
659
660 /**
661 * hb_version_string:
662 *
663 * Returns library version as a string with three components.
664 *
665 * Return value: library version string.
666 *
667 * Since: 0.9.2
668 **/
669 const char *
hb_version_string()670 hb_version_string ()
671 {
672 return HB_VERSION_STRING;
673 }
674
675 /**
676 * hb_version_atleast:
677 * @major:
678 * @minor:
679 * @micro:
680 *
681 *
682 *
683 * Return value:
684 *
685 * Since: 0.9.30
686 **/
687 hb_bool_t
hb_version_atleast(unsigned int major,unsigned int minor,unsigned int micro)688 hb_version_atleast (unsigned int major,
689 unsigned int minor,
690 unsigned int micro)
691 {
692 return HB_VERSION_ATLEAST (major, minor, micro);
693 }
694
695
696
697 /* hb_feature_t and hb_variation_t */
698
699 static bool
parse_space(const char ** pp,const char * end)700 parse_space (const char **pp, const char *end)
701 {
702 while (*pp < end && ISSPACE (**pp))
703 (*pp)++;
704 return true;
705 }
706
707 static bool
parse_char(const char ** pp,const char * end,char c)708 parse_char (const char **pp, const char *end, char c)
709 {
710 parse_space (pp, end);
711
712 if (*pp == end || **pp != c)
713 return false;
714
715 (*pp)++;
716 return true;
717 }
718
719 static bool
parse_uint(const char ** pp,const char * end,unsigned int * pv)720 parse_uint (const char **pp, const char *end, unsigned int *pv)
721 {
722 char buf[32];
723 unsigned int len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
724 strncpy (buf, *pp, len);
725 buf[len] = '\0';
726
727 char *p = buf;
728 char *pend = p;
729 unsigned int v;
730
731 /* Intentionally use strtol instead of strtoul, such that
732 * -1 turns into "big number"... */
733 errno = 0;
734 v = strtol (p, &pend, 10);
735 if (errno || p == pend)
736 return false;
737
738 *pv = v;
739 *pp += pend - p;
740 return true;
741 }
742
743 static bool
parse_uint32(const char ** pp,const char * end,uint32_t * pv)744 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
745 {
746 char buf[32];
747 unsigned int len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
748 strncpy (buf, *pp, len);
749 buf[len] = '\0';
750
751 char *p = buf;
752 char *pend = p;
753 unsigned int v;
754
755 /* Intentionally use strtol instead of strtoul, such that
756 * -1 turns into "big number"... */
757 errno = 0;
758 v = strtol (p, &pend, 10);
759 if (errno || p == pend)
760 return false;
761
762 *pv = v;
763 *pp += pend - p;
764 return true;
765 }
766
767 #if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
768 #define USE_XLOCALE 1
769 #define HB_LOCALE_T locale_t
770 #define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
771 #define HB_FREE_LOCALE(loc) freelocale (loc)
772 #elif defined(_MSC_VER)
773 #define USE_XLOCALE 1
774 #define HB_LOCALE_T _locale_t
775 #define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
776 #define HB_FREE_LOCALE(loc) _free_locale (loc)
777 #define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
778 #endif
779
780 #ifdef USE_XLOCALE
781
782 #if HB_USE_ATEXIT
783 static void free_static_C_locale ();
784 #endif
785
786 static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<HB_LOCALE_T>,
787 hb_C_locale_lazy_loader_t>
788 {
createhb_C_locale_lazy_loader_t789 static HB_LOCALE_T create ()
790 {
791 HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
792
793 #if HB_USE_ATEXIT
794 atexit (free_static_C_locale);
795 #endif
796
797 return C_locale;
798 }
destroyhb_C_locale_lazy_loader_t799 static void destroy (HB_LOCALE_T p)
800 {
801 HB_FREE_LOCALE (p);
802 }
get_nullhb_C_locale_lazy_loader_t803 static HB_LOCALE_T get_null ()
804 {
805 return nullptr;
806 }
807 } static_C_locale;
808
809 #if HB_USE_ATEXIT
810 static
free_static_C_locale()811 void free_static_C_locale ()
812 {
813 static_C_locale.free_instance ();
814 }
815 #endif
816
817 static HB_LOCALE_T
get_C_locale()818 get_C_locale ()
819 {
820 return static_C_locale.get_unconst ();
821 }
822 #endif /* USE_XLOCALE */
823
824 static bool
parse_float(const char ** pp,const char * end,float * pv)825 parse_float (const char **pp, const char *end, float *pv)
826 {
827 char buf[32];
828 unsigned int len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
829 strncpy (buf, *pp, len);
830 buf[len] = '\0';
831
832 char *p = buf;
833 char *pend = p;
834 float v;
835
836 errno = 0;
837 #ifdef USE_XLOCALE
838 v = strtod_l (p, &pend, get_C_locale ());
839 #else
840 v = strtod (p, &pend);
841 #endif
842 if (errno || p == pend)
843 return false;
844
845 *pv = v;
846 *pp += pend - p;
847 return true;
848 }
849
850 static bool
parse_bool(const char ** pp,const char * end,uint32_t * pv)851 parse_bool (const char **pp, const char *end, uint32_t *pv)
852 {
853 parse_space (pp, end);
854
855 const char *p = *pp;
856 while (*pp < end && ISALPHA(**pp))
857 (*pp)++;
858
859 /* CSS allows on/off as aliases 1/0. */
860 if (*pp - p == 2
861 && TOLOWER (p[0]) == 'o'
862 && TOLOWER (p[1]) == 'n')
863 *pv = 1;
864 else if (*pp - p == 3
865 && TOLOWER (p[0]) == 'o'
866 && TOLOWER (p[1]) == 'f'
867 && TOLOWER (p[2]) == 'f')
868 *pv = 0;
869 else
870 return false;
871
872 return true;
873 }
874
875 /* hb_feature_t */
876
877 static bool
parse_feature_value_prefix(const char ** pp,const char * end,hb_feature_t * feature)878 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
879 {
880 if (parse_char (pp, end, '-'))
881 feature->value = 0;
882 else {
883 parse_char (pp, end, '+');
884 feature->value = 1;
885 }
886
887 return true;
888 }
889
890 static bool
parse_tag(const char ** pp,const char * end,hb_tag_t * tag)891 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
892 {
893 parse_space (pp, end);
894
895 char quote = 0;
896
897 if (*pp < end && (**pp == '\'' || **pp == '"'))
898 {
899 quote = **pp;
900 (*pp)++;
901 }
902
903 const char *p = *pp;
904 while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
905 (*pp)++;
906
907 if (p == *pp || *pp - p > 4)
908 return false;
909
910 *tag = hb_tag_from_string (p, *pp - p);
911
912 if (quote)
913 {
914 /* CSS expects exactly four bytes. And we only allow quotations for
915 * CSS compatibility. So, enforce the length. */
916 if (*pp - p != 4)
917 return false;
918 if (*pp == end || **pp != quote)
919 return false;
920 (*pp)++;
921 }
922
923 return true;
924 }
925
926 static bool
parse_feature_indices(const char ** pp,const char * end,hb_feature_t * feature)927 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
928 {
929 parse_space (pp, end);
930
931 bool has_start;
932
933 feature->start = HB_FEATURE_GLOBAL_START;
934 feature->end = HB_FEATURE_GLOBAL_END;
935
936 if (!parse_char (pp, end, '['))
937 return true;
938
939 has_start = parse_uint (pp, end, &feature->start);
940
941 if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
942 parse_uint (pp, end, &feature->end);
943 } else {
944 if (has_start)
945 feature->end = feature->start + 1;
946 }
947
948 return parse_char (pp, end, ']');
949 }
950
951 static bool
parse_feature_value_postfix(const char ** pp,const char * end,hb_feature_t * feature)952 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
953 {
954 bool had_equal = parse_char (pp, end, '=');
955 bool had_value = parse_uint32 (pp, end, &feature->value) ||
956 parse_bool (pp, end, &feature->value);
957 /* CSS doesn't use equal-sign between tag and value.
958 * If there was an equal-sign, then there *must* be a value.
959 * A value without an equal-sign is ok, but not required. */
960 return !had_equal || had_value;
961 }
962
963 static bool
parse_one_feature(const char ** pp,const char * end,hb_feature_t * feature)964 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
965 {
966 return parse_feature_value_prefix (pp, end, feature) &&
967 parse_tag (pp, end, &feature->tag) &&
968 parse_feature_indices (pp, end, feature) &&
969 parse_feature_value_postfix (pp, end, feature) &&
970 parse_space (pp, end) &&
971 *pp == end;
972 }
973
974 /**
975 * hb_feature_from_string:
976 * @str: (array length=len) (element-type uint8_t): a string to parse
977 * @len: length of @str, or -1 if string is %NULL terminated
978 * @feature: (out): the #hb_feature_t to initialize with the parsed values
979 *
980 * Parses a string into a #hb_feature_t.
981 *
982 * The format for specifying feature strings follows. All valid CSS
983 * font-feature-settings values other than 'normal' and the global values are
984 * also accepted, though not documented below. CSS string escapes are not
985 * supported.
986 *
987 * The range indices refer to the positions between Unicode characters. The
988 * position before the first character is always 0.
989 *
990 * The format is Python-esque. Here is how it all works:
991 *
992 * <informaltable pgwide='1' align='left' frame='none'>
993 * <tgroup cols='5'>
994 * <thead>
995 * <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
996 * </thead>
997 * <tbody>
998 * <row><entry>Setting value:</entry></row>
999 * <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
1000 * <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
1001 * <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
1002 * <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
1003 * <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
1004 * <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>
1005 * <row><entry>Setting index:</entry></row>
1006 * <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
1007 * <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
1008 * <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>
1009 * <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>
1010 * <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>
1011 * <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
1012 * <row><entry>Mixing it all:</entry></row>
1013 * <row><entry>aalt[3:5]=2</entry> <entry>2</entry> <entry>3</entry> <entry>5</entry> <entry>Turn 2nd alternate on for range</entry></row>
1014 * </tbody>
1015 * </tgroup>
1016 * </informaltable>
1017 *
1018 * Return value:
1019 * %true if @str is successfully parsed, %false otherwise.
1020 *
1021 * Since: 0.9.5
1022 **/
1023 hb_bool_t
hb_feature_from_string(const char * str,int len,hb_feature_t * feature)1024 hb_feature_from_string (const char *str, int len,
1025 hb_feature_t *feature)
1026 {
1027 hb_feature_t feat;
1028
1029 if (len < 0)
1030 len = strlen (str);
1031
1032 if (likely (parse_one_feature (&str, str + len, &feat)))
1033 {
1034 if (feature)
1035 *feature = feat;
1036 return true;
1037 }
1038
1039 if (feature)
1040 memset (feature, 0, sizeof (*feature));
1041 return false;
1042 }
1043
1044 /**
1045 * hb_feature_to_string:
1046 * @feature: an #hb_feature_t to convert
1047 * @buf: (array length=size) (out): output string
1048 * @size: the allocated size of @buf
1049 *
1050 * Converts a #hb_feature_t into a %NULL-terminated string in the format
1051 * understood by hb_feature_from_string(). The client in responsible for
1052 * allocating big enough size for @buf, 128 bytes is more than enough.
1053 *
1054 * Since: 0.9.5
1055 **/
1056 void
hb_feature_to_string(hb_feature_t * feature,char * buf,unsigned int size)1057 hb_feature_to_string (hb_feature_t *feature,
1058 char *buf, unsigned int size)
1059 {
1060 if (unlikely (!size)) return;
1061
1062 char s[128];
1063 unsigned int len = 0;
1064 if (feature->value == 0)
1065 s[len++] = '-';
1066 hb_tag_to_string (feature->tag, s + len);
1067 len += 4;
1068 while (len && s[len - 1] == ' ')
1069 len--;
1070 if (feature->start != 0 || feature->end != (unsigned int) -1)
1071 {
1072 s[len++] = '[';
1073 if (feature->start)
1074 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
1075 if (feature->end != feature->start + 1) {
1076 s[len++] = ':';
1077 if (feature->end != (unsigned int) -1)
1078 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
1079 }
1080 s[len++] = ']';
1081 }
1082 if (feature->value > 1)
1083 {
1084 s[len++] = '=';
1085 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
1086 }
1087 assert (len < ARRAY_LENGTH (s));
1088 len = hb_min (len, size - 1);
1089 memcpy (buf, s, len);
1090 buf[len] = '\0';
1091 }
1092
1093 /* hb_variation_t */
1094
1095 static bool
parse_variation_value(const char ** pp,const char * end,hb_variation_t * variation)1096 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
1097 {
1098 parse_char (pp, end, '='); /* Optional. */
1099 return parse_float (pp, end, &variation->value);
1100 }
1101
1102 static bool
parse_one_variation(const char ** pp,const char * end,hb_variation_t * variation)1103 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
1104 {
1105 return parse_tag (pp, end, &variation->tag) &&
1106 parse_variation_value (pp, end, variation) &&
1107 parse_space (pp, end) &&
1108 *pp == end;
1109 }
1110
1111 /**
1112 * hb_variation_from_string:
1113 *
1114 * Since: 1.4.2
1115 */
1116 hb_bool_t
hb_variation_from_string(const char * str,int len,hb_variation_t * variation)1117 hb_variation_from_string (const char *str, int len,
1118 hb_variation_t *variation)
1119 {
1120 hb_variation_t var;
1121
1122 if (len < 0)
1123 len = strlen (str);
1124
1125 if (likely (parse_one_variation (&str, str + len, &var)))
1126 {
1127 if (variation)
1128 *variation = var;
1129 return true;
1130 }
1131
1132 if (variation)
1133 memset (variation, 0, sizeof (*variation));
1134 return false;
1135 }
1136
1137 /**
1138 * hb_variation_to_string:
1139 *
1140 * Since: 1.4.2
1141 */
1142 void
hb_variation_to_string(hb_variation_t * variation,char * buf,unsigned int size)1143 hb_variation_to_string (hb_variation_t *variation,
1144 char *buf, unsigned int size)
1145 {
1146 if (unlikely (!size)) return;
1147
1148 char s[128];
1149 unsigned int len = 0;
1150 hb_tag_to_string (variation->tag, s + len);
1151 len += 4;
1152 while (len && s[len - 1] == ' ')
1153 len--;
1154 s[len++] = '=';
1155 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
1156
1157 assert (len < ARRAY_LENGTH (s));
1158 len = hb_min (len, size - 1);
1159 memcpy (buf, s, len);
1160 buf[len] = '\0';
1161 }
1162
1163 /**
1164 * hb_color_get_alpha:
1165 * color: a #hb_color_t we are interested in its channels.
1166 *
1167 * Return value: Alpha channel value of the given color
1168 *
1169 * Since: 2.1.0
1170 */
uint8_t(hb_color_get_alpha)1171 uint8_t
1172 (hb_color_get_alpha) (hb_color_t color)
1173 {
1174 return hb_color_get_alpha (color);
1175 }
1176
1177 /**
1178 * hb_color_get_red:
1179 * color: a #hb_color_t we are interested in its channels.
1180 *
1181 * Return value: Red channel value of the given color
1182 *
1183 * Since: 2.1.0
1184 */
uint8_t(hb_color_get_red)1185 uint8_t
1186 (hb_color_get_red) (hb_color_t color)
1187 {
1188 return hb_color_get_red (color);
1189 }
1190
1191 /**
1192 * hb_color_get_green:
1193 * color: a #hb_color_t we are interested in its channels.
1194 *
1195 * Return value: Green channel value of the given color
1196 *
1197 * Since: 2.1.0
1198 */
uint8_t(hb_color_get_green)1199 uint8_t
1200 (hb_color_get_green) (hb_color_t color)
1201 {
1202 return hb_color_get_green (color);
1203 }
1204
1205 /**
1206 * hb_color_get_blue:
1207 * color: a #hb_color_t we are interested in its channels.
1208 *
1209 * Return value: Blue channel value of the given color
1210 *
1211 * Since: 2.1.0
1212 */
uint8_t(hb_color_get_blue)1213 uint8_t
1214 (hb_color_get_blue) (hb_color_t color)
1215 {
1216 return hb_color_get_blue (color);
1217 }
1218
1219
1220 /* If there is no visibility control, then hb-static.cc will NOT
1221 * define anything. Instead, we get it to define one set in here
1222 * only, so only libharfbuzz.so defines them, not other libs. */
1223 #ifdef HB_NO_VISIBILITY
1224 #undef HB_NO_VISIBILITY
1225 #include "hb-static.cc"
1226 #define HB_NO_VISIBILITY 1
1227 #endif
1228