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