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