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