• 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 #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 = 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 static 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       hb_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     hb_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_language_matches:
446  * @language: The #hb_language_t to work on
447  * @specific: Another #hb_language_t
448  *
449  * Check whether a second language tag is the same or a more
450  * specific version of the provided language tag.  For example,
451  * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR".
452  *
453  * Return value: `true` if languages match, `false` otherwise.
454  *
455  * Since: 5.0.0
456  **/
457 hb_bool_t
hb_language_matches(hb_language_t language,hb_language_t specific)458 hb_language_matches (hb_language_t language,
459 		     hb_language_t specific)
460 {
461   if (language == specific) return true;
462   if (!language || !specific) return false;
463 
464   const char *l = language->s;
465   const char *s = specific->s;
466   unsigned ll = strlen (l);
467   unsigned sl = strlen (s);
468 
469   if (ll > sl)
470     return false;
471 
472   return strncmp (l, s, ll) == 0 &&
473 	 (s[ll] == '\0' || s[ll] == '-');
474 }
475 
476 
477 /* hb_script_t */
478 
479 /**
480  * hb_script_from_iso15924_tag:
481  * @tag: an #hb_tag_t representing an ISO 15924 tag.
482  *
483  * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
484  *
485  * Return value:
486  * An #hb_script_t corresponding to the ISO 15924 tag.
487  *
488  * Since: 0.9.2
489  **/
490 hb_script_t
hb_script_from_iso15924_tag(hb_tag_t tag)491 hb_script_from_iso15924_tag (hb_tag_t tag)
492 {
493   if (unlikely (tag == HB_TAG_NONE))
494     return HB_SCRIPT_INVALID;
495 
496   /* Be lenient, adjust case (one capital letter followed by three small letters) */
497   tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
498 
499   switch (tag) {
500 
501     /* These graduated from the 'Q' private-area codes, but
502      * the old code is still aliased by Unicode, and the Qaai
503      * one in use by ICU. */
504     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
505     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
506 
507     /* Script variants from https://unicode.org/iso15924/ */
508     case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;
509     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
510     case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;
511     case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;
512     case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;
513     case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;
514     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
515     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
516     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
517     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
518     case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
519   }
520 
521   /* If it looks right, just use the tag as a script */
522   if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
523     return (hb_script_t) tag;
524 
525   /* Otherwise, return unknown */
526   return HB_SCRIPT_UNKNOWN;
527 }
528 
529 /**
530  * hb_script_from_string:
531  * @str: (array length=len) (element-type uint8_t): a string representing an
532  *       ISO 15924 tag.
533  * @len: length of the @str, or -1 if it is `NULL`-terminated.
534  *
535  * Converts a string @str representing an ISO 15924 script tag to a
536  * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
537  * hb_script_from_iso15924_tag().
538  *
539  * Return value:
540  * An #hb_script_t corresponding to the ISO 15924 tag.
541  *
542  * Since: 0.9.2
543  **/
544 hb_script_t
hb_script_from_string(const char * str,int len)545 hb_script_from_string (const char *str, int len)
546 {
547   return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
548 }
549 
550 /**
551  * hb_script_to_iso15924_tag:
552  * @script: an #hb_script_t to convert.
553  *
554  * Converts an #hb_script_t to a corresponding ISO 15924 script tag.
555  *
556  * Return value:
557  * An #hb_tag_t representing an ISO 15924 script tag.
558  *
559  * Since: 0.9.2
560  **/
561 hb_tag_t
hb_script_to_iso15924_tag(hb_script_t script)562 hb_script_to_iso15924_tag (hb_script_t script)
563 {
564   return (hb_tag_t) script;
565 }
566 
567 /**
568  * hb_script_get_horizontal_direction:
569  * @script: The #hb_script_t to query
570  *
571  * Fetches the #hb_direction_t of a script when it is
572  * set horizontally. All right-to-left scripts will return
573  * #HB_DIRECTION_RTL. All left-to-right scripts will return
574  * #HB_DIRECTION_LTR.  Scripts that can be written either
575  * horizontally or vertically will return #HB_DIRECTION_INVALID.
576  * Unknown scripts will return #HB_DIRECTION_LTR.
577  *
578  * Return value: The horizontal #hb_direction_t of @script
579  *
580  * Since: 0.9.2
581  **/
582 hb_direction_t
hb_script_get_horizontal_direction(hb_script_t script)583 hb_script_get_horizontal_direction (hb_script_t script)
584 {
585   /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
586   switch ((hb_tag_t) script)
587   {
588     /* Unicode-1.1 additions */
589     case HB_SCRIPT_ARABIC:
590     case HB_SCRIPT_HEBREW:
591 
592     /* Unicode-3.0 additions */
593     case HB_SCRIPT_SYRIAC:
594     case HB_SCRIPT_THAANA:
595 
596     /* Unicode-4.0 additions */
597     case HB_SCRIPT_CYPRIOT:
598 
599     /* Unicode-4.1 additions */
600     case HB_SCRIPT_KHAROSHTHI:
601 
602     /* Unicode-5.0 additions */
603     case HB_SCRIPT_PHOENICIAN:
604     case HB_SCRIPT_NKO:
605 
606     /* Unicode-5.1 additions */
607     case HB_SCRIPT_LYDIAN:
608 
609     /* Unicode-5.2 additions */
610     case HB_SCRIPT_AVESTAN:
611     case HB_SCRIPT_IMPERIAL_ARAMAIC:
612     case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
613     case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
614     case HB_SCRIPT_OLD_SOUTH_ARABIAN:
615     case HB_SCRIPT_OLD_TURKIC:
616     case HB_SCRIPT_SAMARITAN:
617 
618     /* Unicode-6.0 additions */
619     case HB_SCRIPT_MANDAIC:
620 
621     /* Unicode-6.1 additions */
622     case HB_SCRIPT_MEROITIC_CURSIVE:
623     case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
624 
625     /* Unicode-7.0 additions */
626     case HB_SCRIPT_MANICHAEAN:
627     case HB_SCRIPT_MENDE_KIKAKUI:
628     case HB_SCRIPT_NABATAEAN:
629     case HB_SCRIPT_OLD_NORTH_ARABIAN:
630     case HB_SCRIPT_PALMYRENE:
631     case HB_SCRIPT_PSALTER_PAHLAVI:
632 
633     /* Unicode-8.0 additions */
634     case HB_SCRIPT_HATRAN:
635 
636     /* Unicode-9.0 additions */
637     case HB_SCRIPT_ADLAM:
638 
639     /* Unicode-11.0 additions */
640     case HB_SCRIPT_HANIFI_ROHINGYA:
641     case HB_SCRIPT_OLD_SOGDIAN:
642     case HB_SCRIPT_SOGDIAN:
643 
644     /* Unicode-12.0 additions */
645     case HB_SCRIPT_ELYMAIC:
646 
647     /* Unicode-13.0 additions */
648     case HB_SCRIPT_CHORASMIAN:
649     case HB_SCRIPT_YEZIDI:
650 
651     /* Unicode-14.0 additions */
652     case HB_SCRIPT_OLD_UYGHUR:
653 
654       return HB_DIRECTION_RTL;
655 
656 
657     /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
658     case HB_SCRIPT_OLD_HUNGARIAN:
659     case HB_SCRIPT_OLD_ITALIC:
660     case HB_SCRIPT_RUNIC:
661 
662       return HB_DIRECTION_INVALID;
663   }
664 
665   return HB_DIRECTION_LTR;
666 }
667 
668 
669 /* hb_version */
670 
671 
672 /**
673  * SECTION:hb-version
674  * @title: hb-version
675  * @short_description: Information about the version of HarfBuzz in use
676  * @include: hb.h
677  *
678  * These functions and macros allow accessing version of the HarfBuzz
679  * library used at compile- as well as run-time, and to direct code
680  * conditionally based on those versions, again, at compile- or run-time.
681  **/
682 
683 
684 /**
685  * hb_version:
686  * @major: (out): Library major version component
687  * @minor: (out): Library minor version component
688  * @micro: (out): Library micro version component
689  *
690  * Returns library version as three integer components.
691  *
692  * Since: 0.9.2
693  **/
694 void
hb_version(unsigned int * major,unsigned int * minor,unsigned int * micro)695 hb_version (unsigned int *major,
696 	    unsigned int *minor,
697 	    unsigned int *micro)
698 {
699   *major = HB_VERSION_MAJOR;
700   *minor = HB_VERSION_MINOR;
701   *micro = HB_VERSION_MICRO;
702 }
703 
704 /**
705  * hb_version_string:
706  *
707  * Returns library version as a string with three components.
708  *
709  * Return value: Library version string
710  *
711  * Since: 0.9.2
712  **/
713 const char *
hb_version_string()714 hb_version_string ()
715 {
716   return HB_VERSION_STRING;
717 }
718 
719 /**
720  * hb_version_atleast:
721  * @major: Library major version component
722  * @minor: Library minor version component
723  * @micro: Library micro version component
724  *
725  * Tests the library version against a minimum value,
726  * as three integer components.
727  *
728  * Return value: `true` if the library is equal to or greater than
729  * the test value, `false` otherwise
730  *
731  * Since: 0.9.30
732  **/
733 hb_bool_t
hb_version_atleast(unsigned int major,unsigned int minor,unsigned int micro)734 hb_version_atleast (unsigned int major,
735 		    unsigned int minor,
736 		    unsigned int micro)
737 {
738   return HB_VERSION_ATLEAST (major, minor, micro);
739 }
740 
741 
742 
743 /* hb_feature_t and hb_variation_t */
744 
745 static bool
parse_space(const char ** pp,const char * end)746 parse_space (const char **pp, const char *end)
747 {
748   while (*pp < end && ISSPACE (**pp))
749     (*pp)++;
750   return true;
751 }
752 
753 static bool
parse_char(const char ** pp,const char * end,char c)754 parse_char (const char **pp, const char *end, char c)
755 {
756   parse_space (pp, end);
757 
758   if (*pp == end || **pp != c)
759     return false;
760 
761   (*pp)++;
762   return true;
763 }
764 
765 static bool
parse_uint(const char ** pp,const char * end,unsigned int * pv)766 parse_uint (const char **pp, const char *end, unsigned int *pv)
767 {
768   /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
769    * such that -1 turns into "big number"... */
770   int v;
771   if (unlikely (!hb_parse_int (pp, end, &v))) return false;
772 
773   *pv = v;
774   return true;
775 }
776 
777 static bool
parse_uint32(const char ** pp,const char * end,uint32_t * pv)778 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
779 {
780   /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
781    * such that -1 turns into "big number"... */
782   int v;
783   if (unlikely (!hb_parse_int (pp, end, &v))) return false;
784 
785   *pv = v;
786   return true;
787 }
788 
789 static bool
parse_bool(const char ** pp,const char * end,uint32_t * pv)790 parse_bool (const char **pp, const char *end, uint32_t *pv)
791 {
792   parse_space (pp, end);
793 
794   const char *p = *pp;
795   while (*pp < end && ISALPHA(**pp))
796     (*pp)++;
797 
798   /* CSS allows on/off as aliases 1/0. */
799   if (*pp - p == 2
800       && TOLOWER (p[0]) == 'o'
801       && TOLOWER (p[1]) == 'n')
802     *pv = 1;
803   else if (*pp - p == 3
804 	   && TOLOWER (p[0]) == 'o'
805 	   && TOLOWER (p[1]) == 'f'
806 	   && TOLOWER (p[2]) == 'f')
807     *pv = 0;
808   else
809     return false;
810 
811   return true;
812 }
813 
814 /* hb_feature_t */
815 
816 static bool
parse_feature_value_prefix(const char ** pp,const char * end,hb_feature_t * feature)817 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
818 {
819   if (parse_char (pp, end, '-'))
820     feature->value = 0;
821   else {
822     parse_char (pp, end, '+');
823     feature->value = 1;
824   }
825 
826   return true;
827 }
828 
829 static bool
parse_tag(const char ** pp,const char * end,hb_tag_t * tag)830 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
831 {
832   parse_space (pp, end);
833 
834   char quote = 0;
835 
836   if (*pp < end && (**pp == '\'' || **pp == '"'))
837   {
838     quote = **pp;
839     (*pp)++;
840   }
841 
842   const char *p = *pp;
843   while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
844     (*pp)++;
845 
846   if (p == *pp || *pp - p > 4)
847     return false;
848 
849   *tag = hb_tag_from_string (p, *pp - p);
850 
851   if (quote)
852   {
853     /* CSS expects exactly four bytes.  And we only allow quotations for
854      * CSS compatibility.  So, enforce the length. */
855      if (*pp - p != 4)
856        return false;
857     if (*pp == end || **pp != quote)
858       return false;
859     (*pp)++;
860   }
861 
862   return true;
863 }
864 
865 static bool
parse_feature_indices(const char ** pp,const char * end,hb_feature_t * feature)866 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
867 {
868   parse_space (pp, end);
869 
870   bool has_start;
871 
872   feature->start = HB_FEATURE_GLOBAL_START;
873   feature->end = HB_FEATURE_GLOBAL_END;
874 
875   if (!parse_char (pp, end, '['))
876     return true;
877 
878   has_start = parse_uint (pp, end, &feature->start);
879 
880   if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
881     parse_uint (pp, end, &feature->end);
882   } else {
883     if (has_start)
884       feature->end = feature->start + 1;
885   }
886 
887   return parse_char (pp, end, ']');
888 }
889 
890 static bool
parse_feature_value_postfix(const char ** pp,const char * end,hb_feature_t * feature)891 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
892 {
893   bool had_equal = parse_char (pp, end, '=');
894   bool had_value = parse_uint32 (pp, end, &feature->value) ||
895 		   parse_bool (pp, end, &feature->value);
896   /* CSS doesn't use equal-sign between tag and value.
897    * If there was an equal-sign, then there *must* be a value.
898    * A value without an equal-sign is ok, but not required. */
899   return !had_equal || had_value;
900 }
901 
902 static bool
parse_one_feature(const char ** pp,const char * end,hb_feature_t * feature)903 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
904 {
905   return parse_feature_value_prefix (pp, end, feature) &&
906 	 parse_tag (pp, end, &feature->tag) &&
907 	 parse_feature_indices (pp, end, feature) &&
908 	 parse_feature_value_postfix (pp, end, feature) &&
909 	 parse_space (pp, end) &&
910 	 *pp == end;
911 }
912 
913 /**
914  * hb_feature_from_string:
915  * @str: (array length=len) (element-type uint8_t): a string to parse
916  * @len: length of @str, or -1 if string is `NULL` terminated
917  * @feature: (out): the #hb_feature_t to initialize with the parsed values
918  *
919  * Parses a string into a #hb_feature_t.
920  *
921  * The format for specifying feature strings follows. All valid CSS
922  * font-feature-settings values other than 'normal' and the global values are
923  * also accepted, though not documented below. CSS string escapes are not
924  * supported.
925  *
926  * The range indices refer to the positions between Unicode characters. The
927  * position before the first character is always 0.
928  *
929  * The format is Python-esque.  Here is how it all works:
930  *
931  * <informaltable pgwide='1' align='left' frame='none'>
932  * <tgroup cols='5'>
933  * <thead>
934  * <row><entry>Syntax</entry>    <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
935  * </thead>
936  * <tbody>
937  * <row><entry>Setting value:</entry></row>
938  * <row><entry>kern</entry>      <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
939  * <row><entry>+kern</entry>     <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
940  * <row><entry>-kern</entry>     <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
941  * <row><entry>kern=0</entry>    <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
942  * <row><entry>kern=1</entry>    <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
943  * <row><entry>aalt=2</entry>    <entry>2</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Choose 2nd alternate</entry></row>
944  * <row><entry>Setting index:</entry></row>
945  * <row><entry>kern[]</entry>    <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
946  * <row><entry>kern[:]</entry>   <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
947  * <row><entry>kern[5:]</entry>  <entry>1</entry>     <entry>5</entry>      <entry>∞</entry>   <entry>Turn feature on, partial</entry></row>
948  * <row><entry>kern[:5]</entry>  <entry>1</entry>     <entry>0</entry>      <entry>5</entry>   <entry>Turn feature on, partial</entry></row>
949  * <row><entry>kern[3:5]</entry> <entry>1</entry>     <entry>3</entry>      <entry>5</entry>   <entry>Turn feature on, range</entry></row>
950  * <row><entry>kern[3]</entry>   <entry>1</entry>     <entry>3</entry>      <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
951  * <row><entry>Mixing it all:</entry></row>
952  * <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>
953  * </tbody>
954  * </tgroup>
955  * </informaltable>
956  *
957  * Return value:
958  * `true` if @str is successfully parsed, `false` otherwise
959  *
960  * Since: 0.9.5
961  **/
962 hb_bool_t
hb_feature_from_string(const char * str,int len,hb_feature_t * feature)963 hb_feature_from_string (const char *str, int len,
964 			hb_feature_t *feature)
965 {
966   hb_feature_t feat;
967 
968   if (len < 0)
969     len = strlen (str);
970 
971   if (likely (parse_one_feature (&str, str + len, &feat)))
972   {
973     if (feature)
974       *feature = feat;
975     return true;
976   }
977 
978   if (feature)
979     hb_memset (feature, 0, sizeof (*feature));
980   return false;
981 }
982 
983 /**
984  * hb_feature_to_string:
985  * @feature: an #hb_feature_t to convert
986  * @buf: (array length=size) (out): output string
987  * @size: the allocated size of @buf
988  *
989  * Converts a #hb_feature_t into a `NULL`-terminated string in the format
990  * understood by hb_feature_from_string(). The client in responsible for
991  * allocating big enough size for @buf, 128 bytes is more than enough.
992  *
993  * Since: 0.9.5
994  **/
995 void
hb_feature_to_string(hb_feature_t * feature,char * buf,unsigned int size)996 hb_feature_to_string (hb_feature_t *feature,
997 		      char *buf, unsigned int size)
998 {
999   if (unlikely (!size)) return;
1000 
1001   char s[128];
1002   unsigned int len = 0;
1003   if (feature->value == 0)
1004     s[len++] = '-';
1005   hb_tag_to_string (feature->tag, s + len);
1006   len += 4;
1007   while (len && s[len - 1] == ' ')
1008     len--;
1009   if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
1010   {
1011     s[len++] = '[';
1012     if (feature->start)
1013       len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
1014     if (feature->end != feature->start + 1) {
1015       s[len++] = ':';
1016       if (feature->end != HB_FEATURE_GLOBAL_END)
1017 	len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
1018     }
1019     s[len++] = ']';
1020   }
1021   if (feature->value > 1)
1022   {
1023     s[len++] = '=';
1024     len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
1025   }
1026   assert (len < ARRAY_LENGTH (s));
1027   len = hb_min (len, size - 1);
1028   hb_memcpy (buf, s, len);
1029   buf[len] = '\0';
1030 }
1031 
1032 /* hb_variation_t */
1033 
1034 static bool
parse_variation_value(const char ** pp,const char * end,hb_variation_t * variation)1035 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
1036 {
1037   parse_char (pp, end, '='); /* Optional. */
1038   double v;
1039   if (unlikely (!hb_parse_double (pp, end, &v))) return false;
1040 
1041   variation->value = v;
1042   return true;
1043 }
1044 
1045 static bool
parse_one_variation(const char ** pp,const char * end,hb_variation_t * variation)1046 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
1047 {
1048   return parse_tag (pp, end, &variation->tag) &&
1049 	 parse_variation_value (pp, end, variation) &&
1050 	 parse_space (pp, end) &&
1051 	 *pp == end;
1052 }
1053 
1054 /**
1055  * hb_variation_from_string:
1056  * @str: (array length=len) (element-type uint8_t): a string to parse
1057  * @len: length of @str, or -1 if string is `NULL` terminated
1058  * @variation: (out): the #hb_variation_t to initialize with the parsed values
1059  *
1060  * Parses a string into a #hb_variation_t.
1061  *
1062  * The format for specifying variation settings follows. All valid CSS
1063  * font-variation-settings values other than 'normal' and 'inherited' are also
1064  * accepted, though, not documented below.
1065  *
1066  * The format is a tag, optionally followed by an equals sign, followed by a
1067  * number. For example `wght=500`, or `slnt=-7.5`.
1068  *
1069  * Return value:
1070  * `true` if @str is successfully parsed, `false` otherwise
1071  *
1072  * Since: 1.4.2
1073  */
1074 hb_bool_t
hb_variation_from_string(const char * str,int len,hb_variation_t * variation)1075 hb_variation_from_string (const char *str, int len,
1076 			  hb_variation_t *variation)
1077 {
1078   hb_variation_t var;
1079 
1080   if (len < 0)
1081     len = strlen (str);
1082 
1083   if (likely (parse_one_variation (&str, str + len, &var)))
1084   {
1085     if (variation)
1086       *variation = var;
1087     return true;
1088   }
1089 
1090   if (variation)
1091     hb_memset (variation, 0, sizeof (*variation));
1092   return false;
1093 }
1094 
1095 #ifndef HB_NO_SETLOCALE
1096 
1097 static inline void free_static_C_locale ();
1098 
1099 static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,
1100 							   hb_C_locale_lazy_loader_t>
1101 {
createhb_C_locale_lazy_loader_t1102   static hb_locale_t create ()
1103   {
1104     hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
1105     if (!l)
1106       return l;
1107 
1108     hb_atexit (free_static_C_locale);
1109 
1110     return l;
1111   }
destroyhb_C_locale_lazy_loader_t1112   static void destroy (hb_locale_t l)
1113   {
1114     freelocale (l);
1115   }
get_nullhb_C_locale_lazy_loader_t1116   static hb_locale_t get_null ()
1117   {
1118     return (hb_locale_t) 0;
1119   }
1120 } static_C_locale;
1121 
1122 static inline
free_static_C_locale()1123 void free_static_C_locale ()
1124 {
1125   static_C_locale.free_instance ();
1126 }
1127 
1128 static hb_locale_t
get_C_locale()1129 get_C_locale ()
1130 {
1131   return static_C_locale.get_unconst ();
1132 }
1133 
1134 #endif
1135 
1136 /**
1137  * hb_variation_to_string:
1138  * @variation: an #hb_variation_t to convert
1139  * @buf: (array length=size) (out caller-allocates): output string
1140  * @size: the allocated size of @buf
1141  *
1142  * Converts an #hb_variation_t into a `NULL`-terminated string in the format
1143  * understood by hb_variation_from_string(). The client in responsible for
1144  * allocating big enough size for @buf, 128 bytes is more than enough.
1145  *
1146  * Since: 1.4.2
1147  */
1148 void
hb_variation_to_string(hb_variation_t * variation,char * buf,unsigned int size)1149 hb_variation_to_string (hb_variation_t *variation,
1150 			char *buf, unsigned int size)
1151 {
1152   if (unlikely (!size)) return;
1153 
1154   char s[128];
1155   unsigned int len = 0;
1156   hb_tag_to_string (variation->tag, s + len);
1157   len += 4;
1158   while (len && s[len - 1] == ' ')
1159     len--;
1160   s[len++] = '=';
1161 
1162   hb_locale_t oldlocale HB_UNUSED;
1163   oldlocale = hb_uselocale (get_C_locale ());
1164   len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
1165   (void) hb_uselocale (oldlocale);
1166 
1167   assert (len < ARRAY_LENGTH (s));
1168   len = hb_min (len, size - 1);
1169   hb_memcpy (buf, s, len);
1170   buf[len] = '\0';
1171 }
1172 
1173 /**
1174  * hb_color_get_alpha:
1175  * @color: an #hb_color_t we are interested in its channels.
1176  *
1177  * Fetches the alpha channel of the given @color.
1178  *
1179  * Return value: Alpha channel value
1180  *
1181  * Since: 2.1.0
1182  */
uint8_t(hb_color_get_alpha)1183 uint8_t
1184 (hb_color_get_alpha) (hb_color_t color)
1185 {
1186   return hb_color_get_alpha (color);
1187 }
1188 
1189 /**
1190  * hb_color_get_red:
1191  * @color: an #hb_color_t we are interested in its channels.
1192  *
1193  * Fetches the red channel of the given @color.
1194  *
1195  * Return value: Red channel value
1196  *
1197  * Since: 2.1.0
1198  */
uint8_t(hb_color_get_red)1199 uint8_t
1200 (hb_color_get_red) (hb_color_t color)
1201 {
1202   return hb_color_get_red (color);
1203 }
1204 
1205 /**
1206  * hb_color_get_green:
1207  * @color: an #hb_color_t we are interested in its channels.
1208  *
1209  * Fetches the green channel of the given @color.
1210  *
1211  * Return value: Green channel value
1212  *
1213  * Since: 2.1.0
1214  */
uint8_t(hb_color_get_green)1215 uint8_t
1216 (hb_color_get_green) (hb_color_t color)
1217 {
1218   return hb_color_get_green (color);
1219 }
1220 
1221 /**
1222  * hb_color_get_blue:
1223  * @color: an #hb_color_t we are interested in its channels.
1224  *
1225  * Fetches the blue channel of the given @color.
1226  *
1227  * Return value: Blue channel value
1228  *
1229  * Since: 2.1.0
1230  */
uint8_t(hb_color_get_blue)1231 uint8_t
1232 (hb_color_get_blue) (hb_color_t color)
1233 {
1234   return hb_color_get_blue (color);
1235 }
1236 
1237 
1238 /* If there is no visibility control, then hb-static.cc will NOT
1239  * define anything.  Instead, we get it to define one set in here
1240  * only, so only libharfbuzz.so defines them, not other libs. */
1241 #ifdef HB_NO_VISIBILITY
1242 #undef HB_NO_VISIBILITY
1243 #include "hb-static.cc"
1244 #define HB_NO_VISIBILITY 1
1245 #endif
1246