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