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