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