• 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 #include "hb-machinery.hh"
31 
32 #include <locale.h>
33 
34 #ifdef HB_NO_SETLOCALE
35 #define setlocale(Category, Locale) "C"
36 #endif
37 
38 /**
39  * SECTION:hb-common
40  * @title: hb-common
41  * @short_description: Common data types
42  * @include: hb.h
43  *
44  * Common data types used across HarfBuzz are defined here.
45  **/
46 
47 
48 /* hb_options_t */
49 
50 hb_atomic_int_t _hb_options;
51 
52 void
_hb_options_init()53 _hb_options_init ()
54 {
55   hb_options_union_t u;
56   u.i = 0;
57   u.opts.initialized = true;
58 
59   const char *c = getenv ("HB_OPTIONS");
60   if (c)
61   {
62     while (*c)
63     {
64       const char *p = strchr (c, ':');
65       if (!p)
66 	p = c + strlen (c);
67 
68 #define OPTION(name, symbol) \
69 	if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0)
70 
71       OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
72       OPTION ("aat", aat);
73 
74 #undef OPTION
75 
76       c = *p ? p + 1 : p;
77     }
78 
79   }
80 
81   /* This is idempotent and threadsafe. */
82   _hb_options.set_relaxed (u.i);
83 }
84 
85 
86 /* hb_tag_t */
87 
88 /**
89  * hb_tag_from_string:
90  * @str: (array length=len) (element-type uint8_t):
91  * @len:
92  *
93  *
94  *
95  * Return value:
96  *
97  * Since: 0.9.2
98  **/
99 hb_tag_t
hb_tag_from_string(const char * str,int len)100 hb_tag_from_string (const char *str, int len)
101 {
102   char tag[4];
103   unsigned int i;
104 
105   if (!str || !len || !*str)
106     return HB_TAG_NONE;
107 
108   if (len < 0 || len > 4)
109     len = 4;
110   for (i = 0; i < (unsigned) len && str[i]; i++)
111     tag[i] = str[i];
112   for (; i < 4; i++)
113     tag[i] = ' ';
114 
115   return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
116 }
117 
118 /**
119  * hb_tag_to_string:
120  * @tag:
121  * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t):
122  *
123  *
124  *
125  * Since: 0.9.5
126  **/
127 void
hb_tag_to_string(hb_tag_t tag,char * buf)128 hb_tag_to_string (hb_tag_t tag, char *buf)
129 {
130   buf[0] = (char) (uint8_t) (tag >> 24);
131   buf[1] = (char) (uint8_t) (tag >> 16);
132   buf[2] = (char) (uint8_t) (tag >>  8);
133   buf[3] = (char) (uint8_t) (tag >>  0);
134 }
135 
136 
137 /* hb_direction_t */
138 
139 const char direction_strings[][4] = {
140   "ltr",
141   "rtl",
142   "ttb",
143   "btt"
144 };
145 
146 /**
147  * hb_direction_from_string:
148  * @str: (array length=len) (element-type uint8_t):
149  * @len:
150  *
151  *
152  *
153  * Return value:
154  *
155  * Since: 0.9.2
156  **/
157 hb_direction_t
hb_direction_from_string(const char * str,int len)158 hb_direction_from_string (const char *str, int len)
159 {
160   if (unlikely (!str || !len || !*str))
161     return HB_DIRECTION_INVALID;
162 
163   /* Lets match loosely: just match the first letter, such that
164    * all of "ltr", "left-to-right", etc work!
165    */
166   char c = TOLOWER (str[0]);
167   for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
168     if (c == direction_strings[i][0])
169       return (hb_direction_t) (HB_DIRECTION_LTR + i);
170 
171   return HB_DIRECTION_INVALID;
172 }
173 
174 /**
175  * hb_direction_to_string:
176  * @direction:
177  *
178  *
179  *
180  * Return value: (transfer none):
181  *
182  * Since: 0.9.2
183  **/
184 const char *
hb_direction_to_string(hb_direction_t direction)185 hb_direction_to_string (hb_direction_t direction)
186 {
187   if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
188 	      < ARRAY_LENGTH (direction_strings)))
189     return direction_strings[direction - HB_DIRECTION_LTR];
190 
191   return "invalid";
192 }
193 
194 
195 /* hb_language_t */
196 
197 struct hb_language_impl_t {
198   const char s[1];
199 };
200 
201 static const char canon_map[256] = {
202    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
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,
205   '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
206    0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
207   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
208    0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
209   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
210 };
211 
212 static bool
lang_equal(hb_language_t v1,const void * v2)213 lang_equal (hb_language_t  v1,
214 	    const void    *v2)
215 {
216   const unsigned char *p1 = (const unsigned char *) v1;
217   const unsigned char *p2 = (const unsigned char *) v2;
218 
219   while (*p1 && *p1 == canon_map[*p2]) {
220     p1++;
221     p2++;
222   }
223 
224   return *p1 == canon_map[*p2];
225 }
226 
227 #if 0
228 static unsigned int
229 lang_hash (const void *key)
230 {
231   const unsigned char *p = key;
232   unsigned int h = 0;
233   while (canon_map[*p])
234     {
235       h = (h << 5) - h + canon_map[*p];
236       p++;
237     }
238 
239   return h;
240 }
241 #endif
242 
243 
244 struct hb_language_item_t {
245 
246   struct hb_language_item_t *next;
247   hb_language_t lang;
248 
operator ==hb_language_item_t249   bool operator == (const char *s) const
250   { return lang_equal (lang, s); }
251 
operator =hb_language_item_t252   hb_language_item_t & operator = (const char *s) {
253     /* If a custom allocated is used calling strdup() pairs
254     badly with a call to the custom free() in fini() below.
255     Therefore don't call strdup(), implement its behavior.
256     */
257     size_t len = strlen(s) + 1;
258     lang = (hb_language_t) malloc(len);
259     if (likely (lang))
260     {
261       memcpy((unsigned char *) lang, s, len);
262       for (unsigned char *p = (unsigned char *) lang; *p; p++)
263 	*p = canon_map[*p];
264     }
265 
266     return *this;
267   }
268 
finihb_language_item_t269   void fini () { free ((void *) lang); }
270 };
271 
272 
273 /* Thread-safe lock-free language list */
274 
275 static hb_atomic_ptr_t <hb_language_item_t> langs;
276 
277 #if HB_USE_ATEXIT
278 static void
free_langs()279 free_langs ()
280 {
281 retry:
282   hb_language_item_t *first_lang = langs;
283   if (unlikely (!langs.cmpexch (first_lang, nullptr)))
284     goto retry;
285 
286   while (first_lang) {
287     hb_language_item_t *next = first_lang->next;
288     first_lang->fini ();
289     free (first_lang);
290     first_lang = next;
291   }
292 }
293 #endif
294 
295 static hb_language_item_t *
lang_find_or_insert(const char * key)296 lang_find_or_insert (const char *key)
297 {
298 retry:
299   hb_language_item_t *first_lang = langs;
300 
301   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
302     if (*lang == key)
303       return lang;
304 
305   /* Not found; allocate one. */
306   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
307   if (unlikely (!lang))
308     return nullptr;
309   lang->next = first_lang;
310   *lang = key;
311   if (unlikely (!lang->lang))
312   {
313     free (lang);
314     return nullptr;
315   }
316 
317   if (unlikely (!langs.cmpexch (first_lang, lang)))
318   {
319     lang->fini ();
320     free (lang);
321     goto retry;
322   }
323 
324 #if HB_USE_ATEXIT
325   if (!first_lang)
326     atexit (free_langs); /* First person registers atexit() callback. */
327 #endif
328 
329   return lang;
330 }
331 
332 
333 /**
334  * hb_language_from_string:
335  * @str: (array length=len) (element-type uint8_t): a string representing
336  *       a BCP 47 language tag
337  * @len: length of the @str, or -1 if it is %NULL-terminated.
338  *
339  * Converts @str representing a BCP 47 language tag to the corresponding
340  * #hb_language_t.
341  *
342  * Return value: (transfer none):
343  * The #hb_language_t corresponding to the BCP 47 language tag.
344  *
345  * Since: 0.9.2
346  **/
347 hb_language_t
hb_language_from_string(const char * str,int len)348 hb_language_from_string (const char *str, int len)
349 {
350   if (!str || !len || !*str)
351     return HB_LANGUAGE_INVALID;
352 
353   hb_language_item_t *item = nullptr;
354   if (len >= 0)
355   {
356     /* NUL-terminate it. */
357     char strbuf[64];
358     len = hb_min (len, (int) sizeof (strbuf) - 1);
359     memcpy (strbuf, str, len);
360     strbuf[len] = '\0';
361     item = lang_find_or_insert (strbuf);
362   }
363   else
364     item = lang_find_or_insert (str);
365 
366   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
367 }
368 
369 /**
370  * hb_language_to_string:
371  * @language: an #hb_language_t to convert.
372  *
373  * See hb_language_from_string().
374  *
375  * Return value: (transfer none):
376  * A %NULL-terminated string representing the @language. Must not be freed by
377  * the caller.
378  *
379  * Since: 0.9.2
380  **/
381 const char *
hb_language_to_string(hb_language_t language)382 hb_language_to_string (hb_language_t language)
383 {
384   if (unlikely (!language)) return nullptr;
385 
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   /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
723    * such that -1 turns into "big number"... */
724   int v;
725   if (unlikely (!hb_parse_int (pp, end, &v))) return false;
726 
727   *pv = v;
728   return true;
729 }
730 
731 static bool
parse_uint32(const char ** pp,const char * end,uint32_t * pv)732 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
733 {
734   /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
735    * such that -1 turns into "big number"... */
736   int v;
737   if (unlikely (!hb_parse_int (pp, end, &v))) return false;
738 
739   *pv = v;
740   return true;
741 }
742 
743 static bool
parse_bool(const char ** pp,const char * end,uint32_t * pv)744 parse_bool (const char **pp, const char *end, uint32_t *pv)
745 {
746   parse_space (pp, end);
747 
748   const char *p = *pp;
749   while (*pp < end && ISALPHA(**pp))
750     (*pp)++;
751 
752   /* CSS allows on/off as aliases 1/0. */
753   if (*pp - p == 2
754       && TOLOWER (p[0]) == 'o'
755       && TOLOWER (p[1]) == 'n')
756     *pv = 1;
757   else if (*pp - p == 3
758 	   && TOLOWER (p[0]) == 'o'
759 	   && TOLOWER (p[1]) == 'f'
760 	   && TOLOWER (p[2]) == 'f')
761     *pv = 0;
762   else
763     return false;
764 
765   return true;
766 }
767 
768 /* hb_feature_t */
769 
770 static bool
parse_feature_value_prefix(const char ** pp,const char * end,hb_feature_t * feature)771 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
772 {
773   if (parse_char (pp, end, '-'))
774     feature->value = 0;
775   else {
776     parse_char (pp, end, '+');
777     feature->value = 1;
778   }
779 
780   return true;
781 }
782 
783 static bool
parse_tag(const char ** pp,const char * end,hb_tag_t * tag)784 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
785 {
786   parse_space (pp, end);
787 
788   char quote = 0;
789 
790   if (*pp < end && (**pp == '\'' || **pp == '"'))
791   {
792     quote = **pp;
793     (*pp)++;
794   }
795 
796   const char *p = *pp;
797   while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
798     (*pp)++;
799 
800   if (p == *pp || *pp - p > 4)
801     return false;
802 
803   *tag = hb_tag_from_string (p, *pp - p);
804 
805   if (quote)
806   {
807     /* CSS expects exactly four bytes.  And we only allow quotations for
808      * CSS compatibility.  So, enforce the length. */
809      if (*pp - p != 4)
810        return false;
811     if (*pp == end || **pp != quote)
812       return false;
813     (*pp)++;
814   }
815 
816   return true;
817 }
818 
819 static bool
parse_feature_indices(const char ** pp,const char * end,hb_feature_t * feature)820 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
821 {
822   parse_space (pp, end);
823 
824   bool has_start;
825 
826   feature->start = HB_FEATURE_GLOBAL_START;
827   feature->end = HB_FEATURE_GLOBAL_END;
828 
829   if (!parse_char (pp, end, '['))
830     return true;
831 
832   has_start = parse_uint (pp, end, &feature->start);
833 
834   if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
835     parse_uint (pp, end, &feature->end);
836   } else {
837     if (has_start)
838       feature->end = feature->start + 1;
839   }
840 
841   return parse_char (pp, end, ']');
842 }
843 
844 static bool
parse_feature_value_postfix(const char ** pp,const char * end,hb_feature_t * feature)845 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
846 {
847   bool had_equal = parse_char (pp, end, '=');
848   bool had_value = parse_uint32 (pp, end, &feature->value) ||
849 		   parse_bool (pp, end, &feature->value);
850   /* CSS doesn't use equal-sign between tag and value.
851    * If there was an equal-sign, then there *must* be a value.
852    * A value without an equal-sign is ok, but not required. */
853   return !had_equal || had_value;
854 }
855 
856 static bool
parse_one_feature(const char ** pp,const char * end,hb_feature_t * feature)857 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
858 {
859   return parse_feature_value_prefix (pp, end, feature) &&
860 	 parse_tag (pp, end, &feature->tag) &&
861 	 parse_feature_indices (pp, end, feature) &&
862 	 parse_feature_value_postfix (pp, end, feature) &&
863 	 parse_space (pp, end) &&
864 	 *pp == end;
865 }
866 
867 /**
868  * hb_feature_from_string:
869  * @str: (array length=len) (element-type uint8_t): a string to parse
870  * @len: length of @str, or -1 if string is %NULL terminated
871  * @feature: (out): the #hb_feature_t to initialize with the parsed values
872  *
873  * Parses a string into a #hb_feature_t.
874  *
875  * The format for specifying feature strings follows. All valid CSS
876  * font-feature-settings values other than 'normal' and the global values are
877  * also accepted, though not documented below. CSS string escapes are not
878  * supported.
879  *
880  * The range indices refer to the positions between Unicode characters. The
881  * position before the first character is always 0.
882  *
883  * The format is Python-esque.  Here is how it all works:
884  *
885  * <informaltable pgwide='1' align='left' frame='none'>
886  * <tgroup cols='5'>
887  * <thead>
888  * <row><entry>Syntax</entry>    <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
889  * </thead>
890  * <tbody>
891  * <row><entry>Setting value:</entry></row>
892  * <row><entry>kern</entry>      <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
893  * <row><entry>+kern</entry>     <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
894  * <row><entry>-kern</entry>     <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
895  * <row><entry>kern=0</entry>    <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
896  * <row><entry>kern=1</entry>    <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
897  * <row><entry>aalt=2</entry>    <entry>2</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Choose 2nd alternate</entry></row>
898  * <row><entry>Setting index:</entry></row>
899  * <row><entry>kern[]</entry>    <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
900  * <row><entry>kern[:]</entry>   <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
901  * <row><entry>kern[5:]</entry>  <entry>1</entry>     <entry>5</entry>      <entry>∞</entry>   <entry>Turn feature on, partial</entry></row>
902  * <row><entry>kern[:5]</entry>  <entry>1</entry>     <entry>0</entry>      <entry>5</entry>   <entry>Turn feature on, partial</entry></row>
903  * <row><entry>kern[3:5]</entry> <entry>1</entry>     <entry>3</entry>      <entry>5</entry>   <entry>Turn feature on, range</entry></row>
904  * <row><entry>kern[3]</entry>   <entry>1</entry>     <entry>3</entry>      <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
905  * <row><entry>Mixing it all:</entry></row>
906  * <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>
907  * </tbody>
908  * </tgroup>
909  * </informaltable>
910  *
911  * Return value:
912  * %true if @str is successfully parsed, %false otherwise.
913  *
914  * Since: 0.9.5
915  **/
916 hb_bool_t
hb_feature_from_string(const char * str,int len,hb_feature_t * feature)917 hb_feature_from_string (const char *str, int len,
918 			hb_feature_t *feature)
919 {
920   hb_feature_t feat;
921 
922   if (len < 0)
923     len = strlen (str);
924 
925   if (likely (parse_one_feature (&str, str + len, &feat)))
926   {
927     if (feature)
928       *feature = feat;
929     return true;
930   }
931 
932   if (feature)
933     memset (feature, 0, sizeof (*feature));
934   return false;
935 }
936 
937 /**
938  * hb_feature_to_string:
939  * @feature: an #hb_feature_t to convert
940  * @buf: (array length=size) (out): output string
941  * @size: the allocated size of @buf
942  *
943  * Converts a #hb_feature_t into a %NULL-terminated string in the format
944  * understood by hb_feature_from_string(). The client in responsible for
945  * allocating big enough size for @buf, 128 bytes is more than enough.
946  *
947  * Since: 0.9.5
948  **/
949 void
hb_feature_to_string(hb_feature_t * feature,char * buf,unsigned int size)950 hb_feature_to_string (hb_feature_t *feature,
951 		      char *buf, unsigned int size)
952 {
953   if (unlikely (!size)) return;
954 
955   char s[128];
956   unsigned int len = 0;
957   if (feature->value == 0)
958     s[len++] = '-';
959   hb_tag_to_string (feature->tag, s + len);
960   len += 4;
961   while (len && s[len - 1] == ' ')
962     len--;
963   if (feature->start != 0 || feature->end != (unsigned int) -1)
964   {
965     s[len++] = '[';
966     if (feature->start)
967       len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
968     if (feature->end != feature->start + 1) {
969       s[len++] = ':';
970       if (feature->end != (unsigned int) -1)
971 	len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
972     }
973     s[len++] = ']';
974   }
975   if (feature->value > 1)
976   {
977     s[len++] = '=';
978     len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
979   }
980   assert (len < ARRAY_LENGTH (s));
981   len = hb_min (len, size - 1);
982   memcpy (buf, s, len);
983   buf[len] = '\0';
984 }
985 
986 /* hb_variation_t */
987 
988 static bool
parse_variation_value(const char ** pp,const char * end,hb_variation_t * variation)989 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
990 {
991   parse_char (pp, end, '='); /* Optional. */
992   double v;
993   if (unlikely (!hb_parse_double (pp, end, &v))) return false;
994 
995   variation->value = v;
996   return true;
997 }
998 
999 static bool
parse_one_variation(const char ** pp,const char * end,hb_variation_t * variation)1000 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
1001 {
1002   return parse_tag (pp, end, &variation->tag) &&
1003 	 parse_variation_value (pp, end, variation) &&
1004 	 parse_space (pp, end) &&
1005 	 *pp == end;
1006 }
1007 
1008 /**
1009  * hb_variation_from_string:
1010  *
1011  * Since: 1.4.2
1012  */
1013 hb_bool_t
hb_variation_from_string(const char * str,int len,hb_variation_t * variation)1014 hb_variation_from_string (const char *str, int len,
1015 			  hb_variation_t *variation)
1016 {
1017   hb_variation_t var;
1018 
1019   if (len < 0)
1020     len = strlen (str);
1021 
1022   if (likely (parse_one_variation (&str, str + len, &var)))
1023   {
1024     if (variation)
1025       *variation = var;
1026     return true;
1027   }
1028 
1029   if (variation)
1030     memset (variation, 0, sizeof (*variation));
1031   return false;
1032 }
1033 
1034 /**
1035  * hb_variation_to_string:
1036  *
1037  * Since: 1.4.2
1038  */
1039 void
hb_variation_to_string(hb_variation_t * variation,char * buf,unsigned int size)1040 hb_variation_to_string (hb_variation_t *variation,
1041 			char *buf, unsigned int size)
1042 {
1043   if (unlikely (!size)) return;
1044 
1045   char s[128];
1046   unsigned int len = 0;
1047   hb_tag_to_string (variation->tag, s + len);
1048   len += 4;
1049   while (len && s[len - 1] == ' ')
1050     len--;
1051   s[len++] = '=';
1052   len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
1053 
1054   assert (len < ARRAY_LENGTH (s));
1055   len = hb_min (len, size - 1);
1056   memcpy (buf, s, len);
1057   buf[len] = '\0';
1058 }
1059 
1060 /**
1061  * hb_color_get_alpha:
1062  * color: a #hb_color_t we are interested in its channels.
1063  *
1064  * Return value: Alpha channel value of the given color
1065  *
1066  * Since: 2.1.0
1067  */
uint8_t(hb_color_get_alpha)1068 uint8_t
1069 (hb_color_get_alpha) (hb_color_t color)
1070 {
1071   return hb_color_get_alpha (color);
1072 }
1073 
1074 /**
1075  * hb_color_get_red:
1076  * color: a #hb_color_t we are interested in its channels.
1077  *
1078  * Return value: Red channel value of the given color
1079  *
1080  * Since: 2.1.0
1081  */
uint8_t(hb_color_get_red)1082 uint8_t
1083 (hb_color_get_red) (hb_color_t color)
1084 {
1085   return hb_color_get_red (color);
1086 }
1087 
1088 /**
1089  * hb_color_get_green:
1090  * color: a #hb_color_t we are interested in its channels.
1091  *
1092  * Return value: Green channel value of the given color
1093  *
1094  * Since: 2.1.0
1095  */
uint8_t(hb_color_get_green)1096 uint8_t
1097 (hb_color_get_green) (hb_color_t color)
1098 {
1099   return hb_color_get_green (color);
1100 }
1101 
1102 /**
1103  * hb_color_get_blue:
1104  * color: a #hb_color_t we are interested in its channels.
1105  *
1106  * Return value: Blue channel value of the given color
1107  *
1108  * Since: 2.1.0
1109  */
uint8_t(hb_color_get_blue)1110 uint8_t
1111 (hb_color_get_blue) (hb_color_t color)
1112 {
1113   return hb_color_get_blue (color);
1114 }
1115 
1116 
1117 /* If there is no visibility control, then hb-static.cc will NOT
1118  * define anything.  Instead, we get it to define one set in here
1119  * only, so only libharfbuzz.so defines them, not other libs. */
1120 #ifdef HB_NO_VISIBILITY
1121 #undef HB_NO_VISIBILITY
1122 #include "hb-static.cc"
1123 #define HB_NO_VISIBILITY 1
1124 #endif
1125