• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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