src/java.desktop/share/native/libfontmanager/harfbuzz/hb-common.cc
changeset 48274 51772bf1fb0c
parent 47216 71c04702a3d5
child 50352 25db2c8f3cf8
equal deleted inserted replaced
48273:e2065f7505eb 48274:51772bf1fb0c
    30 
    30 
    31 #include "hb-mutex-private.hh"
    31 #include "hb-mutex-private.hh"
    32 #include "hb-object-private.hh"
    32 #include "hb-object-private.hh"
    33 
    33 
    34 #include <locale.h>
    34 #include <locale.h>
       
    35 #ifdef HAVE_XLOCALE_H
       
    36 #include <xlocale.h>
       
    37 #endif
    35 
    38 
    36 
    39 
    37 /* hb_options_t */
    40 /* hb_options_t */
    38 
    41 
    39 hb_options_union_t _hb_options;
    42 hb_options_union_t _hb_options;
    55 
    58 
    56 /* hb_tag_t */
    59 /* hb_tag_t */
    57 
    60 
    58 /**
    61 /**
    59  * hb_tag_from_string:
    62  * hb_tag_from_string:
    60  * @str: (array length=len) (element-type uint8_t): 
    63  * @str: (array length=len) (element-type uint8_t):
    61  * @len: 
    64  * @len:
    62  *
    65  *
    63  * 
    66  *
    64  *
    67  *
    65  * Return value: 
    68  * Return value:
    66  *
    69  *
    67  * Since: 0.9.2
    70  * Since: 0.9.2
    68  **/
    71  **/
    69 hb_tag_t
    72 hb_tag_t
    70 hb_tag_from_string (const char *str, int len)
    73 hb_tag_from_string (const char *str, int len)
    80   for (i = 0; i < (unsigned) len && str[i]; i++)
    83   for (i = 0; i < (unsigned) len && str[i]; i++)
    81     tag[i] = str[i];
    84     tag[i] = str[i];
    82   for (; i < 4; i++)
    85   for (; i < 4; i++)
    83     tag[i] = ' ';
    86     tag[i] = ' ';
    84 
    87 
    85   return HB_TAG_CHAR4 (tag);
    88   return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
    86 }
    89 }
    87 
    90 
    88 /**
    91 /**
    89  * hb_tag_to_string:
    92  * hb_tag_to_string:
    90  * @tag: 
    93  * @tag:
    91  * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): 
    94  * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t):
    92  *
    95  *
    93  * 
    96  *
    94  *
    97  *
    95  * Since: 0.9.5
    98  * Since: 0.9.5
    96  **/
    99  **/
    97 void
   100 void
    98 hb_tag_to_string (hb_tag_t tag, char *buf)
   101 hb_tag_to_string (hb_tag_t tag, char *buf)
   113   "btt"
   116   "btt"
   114 };
   117 };
   115 
   118 
   116 /**
   119 /**
   117  * hb_direction_from_string:
   120  * hb_direction_from_string:
   118  * @str: (array length=len) (element-type uint8_t): 
   121  * @str: (array length=len) (element-type uint8_t):
   119  * @len: 
   122  * @len:
   120  *
   123  *
   121  * 
   124  *
   122  *
   125  *
   123  * Return value: 
   126  * Return value:
   124  *
   127  *
   125  * Since: 0.9.2
   128  * Since: 0.9.2
   126  **/
   129  **/
   127 hb_direction_t
   130 hb_direction_t
   128 hb_direction_from_string (const char *str, int len)
   131 hb_direction_from_string (const char *str, int len)
   141   return HB_DIRECTION_INVALID;
   144   return HB_DIRECTION_INVALID;
   142 }
   145 }
   143 
   146 
   144 /**
   147 /**
   145  * hb_direction_to_string:
   148  * hb_direction_to_string:
   146  * @direction: 
   149  * @direction:
   147  *
   150  *
   148  * 
   151  *
   149  *
   152  *
   150  * Return value: (transfer none): 
   153  * Return value: (transfer none):
   151  *
   154  *
   152  * Since: 0.9.2
   155  * Since: 0.9.2
   153  **/
   156  **/
   154 const char *
   157 const char *
   155 hb_direction_to_string (hb_direction_t direction)
   158 hb_direction_to_string (hb_direction_t direction)
   184             const void    *v2)
   187             const void    *v2)
   185 {
   188 {
   186   const unsigned char *p1 = (const unsigned char *) v1;
   189   const unsigned char *p1 = (const unsigned char *) v1;
   187   const unsigned char *p2 = (const unsigned char *) v2;
   190   const unsigned char *p2 = (const unsigned char *) v2;
   188 
   191 
   189   while (*p1 && *p1 == canon_map[*p2])
   192   while (*p1 && *p1 == canon_map[*p2]) {
   190     p1++, p2++;
   193     p1++;
       
   194     p2++;
       
   195   }
   191 
   196 
   192   return *p1 == canon_map[*p2];
   197   return *p1 == canon_map[*p2];
   193 }
   198 }
   194 
   199 
   195 #if 0
   200 #if 0
   217   inline bool operator == (const char *s) const {
   222   inline bool operator == (const char *s) const {
   218     return lang_equal (lang, s);
   223     return lang_equal (lang, s);
   219   }
   224   }
   220 
   225 
   221   inline hb_language_item_t & operator = (const char *s) {
   226   inline hb_language_item_t & operator = (const char *s) {
   222     lang = (hb_language_t) strdup (s);
   227     /* If a custom allocated is used calling strdup() pairs
   223     for (unsigned char *p = (unsigned char *) lang; *p; p++)
   228     badly with a call to the custom free() in finish() below.
   224       *p = canon_map[*p];
   229     Therefore don't call strdup(), implement its behavior.
       
   230     */
       
   231     size_t len = strlen(s) + 1;
       
   232     lang = (hb_language_t) malloc(len);
       
   233     if (likely (lang))
       
   234     {
       
   235       memcpy((unsigned char *) lang, s, len);
       
   236       for (unsigned char *p = (unsigned char *) lang; *p; p++)
       
   237         *p = canon_map[*p];
       
   238     }
   225 
   239 
   226     return *this;
   240     return *this;
   227   }
   241   }
   228 
   242 
   229   void finish (void) { free ((void *) lang); }
   243   void finish (void) { free ((void *) lang); }
   233 /* Thread-safe lock-free language list */
   247 /* Thread-safe lock-free language list */
   234 
   248 
   235 static hb_language_item_t *langs;
   249 static hb_language_item_t *langs;
   236 
   250 
   237 #ifdef HB_USE_ATEXIT
   251 #ifdef HB_USE_ATEXIT
   238 static
   252 static void
   239 void free_langs (void)
   253 free_langs (void)
   240 {
   254 {
   241   while (langs) {
   255   while (langs) {
   242     hb_language_item_t *next = langs->next;
   256     hb_language_item_t *next = langs->next;
   243     langs->finish ();
   257     langs->finish ();
   244     free (langs);
   258     free (langs);
   258       return lang;
   272       return lang;
   259 
   273 
   260   /* Not found; allocate one. */
   274   /* Not found; allocate one. */
   261   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
   275   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
   262   if (unlikely (!lang))
   276   if (unlikely (!lang))
   263     return NULL;
   277     return nullptr;
   264   lang->next = first_lang;
   278   lang->next = first_lang;
   265   *lang = key;
   279   *lang = key;
       
   280   if (unlikely (!lang->lang))
       
   281   {
       
   282     free (lang);
       
   283     return nullptr;
       
   284   }
   266 
   285 
   267   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
   286   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
   268     lang->finish ();
   287     lang->finish ();
   269     free (lang);
   288     free (lang);
   270     goto retry;
   289     goto retry;
   297 hb_language_from_string (const char *str, int len)
   316 hb_language_from_string (const char *str, int len)
   298 {
   317 {
   299   if (!str || !len || !*str)
   318   if (!str || !len || !*str)
   300     return HB_LANGUAGE_INVALID;
   319     return HB_LANGUAGE_INVALID;
   301 
   320 
   302   hb_language_item_t *item = NULL;
   321   hb_language_item_t *item = nullptr;
   303   if (len >= 0)
   322   if (len >= 0)
   304   {
   323   {
   305     /* NUL-terminate it. */
   324     /* NUL-terminate it. */
   306     char strbuf[64];
   325     char strbuf[64];
   307     len = MIN (len, (int) sizeof (strbuf) - 1);
   326     len = MIN (len, (int) sizeof (strbuf) - 1);
   328  * Since: 0.9.2
   347  * Since: 0.9.2
   329  **/
   348  **/
   330 const char *
   349 const char *
   331 hb_language_to_string (hb_language_t language)
   350 hb_language_to_string (hb_language_t language)
   332 {
   351 {
   333   /* This is actually NULL-safe! */
   352   /* This is actually nullptr-safe! */
   334   return language->s;
   353   return language->s;
   335 }
   354 }
   336 
   355 
   337 /**
   356 /**
   338  * hb_language_get_default:
   357  * hb_language_get_default:
   339  *
   358  *
   340  * 
   359  *
   341  *
   360  *
   342  * Return value: (transfer none):
   361  * Return value: (transfer none):
   343  *
   362  *
   344  * Since: 0.9.2
   363  * Since: 0.9.2
   345  **/
   364  **/
   348 {
   367 {
   349   static hb_language_t default_language = HB_LANGUAGE_INVALID;
   368   static hb_language_t default_language = HB_LANGUAGE_INVALID;
   350 
   369 
   351   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
   370   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
   352   if (unlikely (language == HB_LANGUAGE_INVALID)) {
   371   if (unlikely (language == HB_LANGUAGE_INVALID)) {
   353     language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
   372     language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
   354     (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
   373     (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
   355   }
   374   }
   356 
   375 
   357   return default_language;
   376   return default_language;
   358 }
   377 }
   364  * hb_script_from_iso15924_tag:
   383  * hb_script_from_iso15924_tag:
   365  * @tag: an #hb_tag_t representing an ISO 15924 tag.
   384  * @tag: an #hb_tag_t representing an ISO 15924 tag.
   366  *
   385  *
   367  * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
   386  * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
   368  *
   387  *
   369  * Return value: 
   388  * Return value:
   370  * An #hb_script_t corresponding to the ISO 15924 tag.
   389  * An #hb_script_t corresponding to the ISO 15924 tag.
   371  *
   390  *
   372  * Since: 0.9.2
   391  * Since: 0.9.2
   373  **/
   392  **/
   374 hb_script_t
   393 hb_script_t
   413  *
   432  *
   414  * Converts a string @str representing an ISO 15924 script tag to a
   433  * Converts a string @str representing an ISO 15924 script tag to a
   415  * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
   434  * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
   416  * hb_script_from_iso15924_tag().
   435  * hb_script_from_iso15924_tag().
   417  *
   436  *
   418  * Return value: 
   437  * Return value:
   419  * An #hb_script_t corresponding to the ISO 15924 tag.
   438  * An #hb_script_t corresponding to the ISO 15924 tag.
   420  *
   439  *
   421  * Since: 0.9.2
   440  * Since: 0.9.2
   422  **/
   441  **/
   423 hb_script_t
   442 hb_script_t
   443   return (hb_tag_t) script;
   462   return (hb_tag_t) script;
   444 }
   463 }
   445 
   464 
   446 /**
   465 /**
   447  * hb_script_get_horizontal_direction:
   466  * hb_script_get_horizontal_direction:
   448  * @script: 
   467  * @script:
   449  *
   468  *
   450  * 
   469  *
   451  *
   470  *
   452  * Return value: 
   471  * Return value:
   453  *
   472  *
   454  * Since: 0.9.2
   473  * Since: 0.9.2
   455  **/
   474  **/
   456 hb_direction_t
   475 hb_direction_t
   457 hb_script_get_horizontal_direction (hb_script_t script)
   476 hb_script_get_horizontal_direction (hb_script_t script)
   541 }
   560 }
   542 
   561 
   543 void *
   562 void *
   544 hb_user_data_array_t::get (hb_user_data_key_t *key)
   563 hb_user_data_array_t::get (hb_user_data_key_t *key)
   545 {
   564 {
   546   hb_user_data_item_t item = {NULL, NULL, NULL};
   565   hb_user_data_item_t item = {nullptr, nullptr, nullptr};
   547 
   566 
   548   return items.find (key, &item, lock) ? item.data : NULL;
   567   return items.find (key, &item, lock) ? item.data : nullptr;
   549 }
   568 }
   550 
   569 
   551 
   570 
   552 /* hb_version */
   571 /* hb_version */
   553 
   572 
   586   return HB_VERSION_STRING;
   605   return HB_VERSION_STRING;
   587 }
   606 }
   588 
   607 
   589 /**
   608 /**
   590  * hb_version_atleast:
   609  * hb_version_atleast:
   591  * @major: 
   610  * @major:
   592  * @minor: 
   611  * @minor:
   593  * @micro: 
   612  * @micro:
   594  *
   613  *
   595  * 
   614  *
   596  *
   615  *
   597  * Return value: 
   616  * Return value:
   598  *
   617  *
   599  * Since: 0.9.30
   618  * Since: 0.9.30
   600  **/
   619  **/
   601 hb_bool_t
   620 hb_bool_t
   602 hb_version_atleast (unsigned int major,
   621 hb_version_atleast (unsigned int major,
   603                     unsigned int minor,
   622                     unsigned int minor,
   604                     unsigned int micro)
   623                     unsigned int micro)
   605 {
   624 {
   606   return HB_VERSION_ATLEAST (major, minor, micro);
   625   return HB_VERSION_ATLEAST (major, minor, micro);
   607 }
   626 }
       
   627 
       
   628 
       
   629 
       
   630 /* hb_feature_t and hb_variation_t */
       
   631 
       
   632 static bool
       
   633 parse_space (const char **pp, const char *end)
       
   634 {
       
   635   while (*pp < end && ISSPACE (**pp))
       
   636     (*pp)++;
       
   637   return true;
       
   638 }
       
   639 
       
   640 static bool
       
   641 parse_char (const char **pp, const char *end, char c)
       
   642 {
       
   643   parse_space (pp, end);
       
   644 
       
   645   if (*pp == end || **pp != c)
       
   646     return false;
       
   647 
       
   648   (*pp)++;
       
   649   return true;
       
   650 }
       
   651 
       
   652 static bool
       
   653 parse_uint (const char **pp, const char *end, unsigned int *pv)
       
   654 {
       
   655   char buf[32];
       
   656   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
       
   657   strncpy (buf, *pp, len);
       
   658   buf[len] = '\0';
       
   659 
       
   660   char *p = buf;
       
   661   char *pend = p;
       
   662   unsigned int v;
       
   663 
       
   664   /* Intentionally use strtol instead of strtoul, such that
       
   665    * -1 turns into "big number"... */
       
   666   errno = 0;
       
   667   v = strtol (p, &pend, 0);
       
   668   if (errno || p == pend)
       
   669     return false;
       
   670 
       
   671   *pv = v;
       
   672   *pp += pend - p;
       
   673   return true;
       
   674 }
       
   675 
       
   676 static bool
       
   677 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
       
   678 {
       
   679   char buf[32];
       
   680   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
       
   681   strncpy (buf, *pp, len);
       
   682   buf[len] = '\0';
       
   683 
       
   684   char *p = buf;
       
   685   char *pend = p;
       
   686   unsigned int v;
       
   687 
       
   688   /* Intentionally use strtol instead of strtoul, such that
       
   689    * -1 turns into "big number"... */
       
   690   errno = 0;
       
   691   v = strtol (p, &pend, 0);
       
   692   if (errno || p == pend)
       
   693     return false;
       
   694 
       
   695   *pv = v;
       
   696   *pp += pend - p;
       
   697   return true;
       
   698 }
       
   699 
       
   700 #if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
       
   701 #define USE_XLOCALE 1
       
   702 #define HB_LOCALE_T locale_t
       
   703 #define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
       
   704 #define HB_FREE_LOCALE(loc) freelocale (loc)
       
   705 #elif defined(_MSC_VER)
       
   706 #define USE_XLOCALE 1
       
   707 #define HB_LOCALE_T _locale_t
       
   708 #define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
       
   709 #define HB_FREE_LOCALE(loc) _free_locale (loc)
       
   710 #define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
       
   711 #endif
       
   712 
       
   713 #ifdef USE_XLOCALE
       
   714 
       
   715 static HB_LOCALE_T C_locale;
       
   716 
       
   717 #ifdef HB_USE_ATEXIT
       
   718 static void
       
   719 free_C_locale (void)
       
   720 {
       
   721   if (C_locale)
       
   722     HB_FREE_LOCALE (C_locale);
       
   723 }
       
   724 #endif
       
   725 
       
   726 static HB_LOCALE_T
       
   727 get_C_locale (void)
       
   728 {
       
   729 retry:
       
   730   HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
       
   731 
       
   732   if (unlikely (!C))
       
   733   {
       
   734     C = HB_CREATE_LOCALE ("C");
       
   735 
       
   736     if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C))
       
   737     {
       
   738       HB_FREE_LOCALE (C_locale);
       
   739       goto retry;
       
   740     }
       
   741 
       
   742 #ifdef HB_USE_ATEXIT
       
   743     atexit (free_C_locale); /* First person registers atexit() callback. */
       
   744 #endif
       
   745   }
       
   746 
       
   747   return C;
       
   748 }
       
   749 #endif
       
   750 
       
   751 static bool
       
   752 parse_float (const char **pp, const char *end, float *pv)
       
   753 {
       
   754   char buf[32];
       
   755   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
       
   756   strncpy (buf, *pp, len);
       
   757   buf[len] = '\0';
       
   758 
       
   759   char *p = buf;
       
   760   char *pend = p;
       
   761   float v;
       
   762 
       
   763   errno = 0;
       
   764 #ifdef USE_XLOCALE
       
   765   v = strtod_l (p, &pend, get_C_locale ());
       
   766 #else
       
   767   v = strtod (p, &pend);
       
   768 #endif
       
   769   if (errno || p == pend)
       
   770     return false;
       
   771 
       
   772   *pv = v;
       
   773   *pp += pend - p;
       
   774   return true;
       
   775 }
       
   776 
       
   777 static bool
       
   778 parse_bool (const char **pp, const char *end, uint32_t *pv)
       
   779 {
       
   780   parse_space (pp, end);
       
   781 
       
   782   const char *p = *pp;
       
   783   while (*pp < end && ISALPHA(**pp))
       
   784     (*pp)++;
       
   785 
       
   786   /* CSS allows on/off as aliases 1/0. */
       
   787   if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
       
   788     *pv = 1;
       
   789   else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
       
   790     *pv = 0;
       
   791   else
       
   792     return false;
       
   793 
       
   794   return true;
       
   795 }
       
   796 
       
   797 /* hb_feature_t */
       
   798 
       
   799 static bool
       
   800 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
       
   801 {
       
   802   if (parse_char (pp, end, '-'))
       
   803     feature->value = 0;
       
   804   else {
       
   805     parse_char (pp, end, '+');
       
   806     feature->value = 1;
       
   807   }
       
   808 
       
   809   return true;
       
   810 }
       
   811 
       
   812 static bool
       
   813 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
       
   814 {
       
   815   parse_space (pp, end);
       
   816 
       
   817   char quote = 0;
       
   818 
       
   819   if (*pp < end && (**pp == '\'' || **pp == '"'))
       
   820   {
       
   821     quote = **pp;
       
   822     (*pp)++;
       
   823   }
       
   824 
       
   825   const char *p = *pp;
       
   826   while (*pp < end && ISALNUM(**pp))
       
   827     (*pp)++;
       
   828 
       
   829   if (p == *pp || *pp - p > 4)
       
   830     return false;
       
   831 
       
   832   *tag = hb_tag_from_string (p, *pp - p);
       
   833 
       
   834   if (quote)
       
   835   {
       
   836     /* CSS expects exactly four bytes.  And we only allow quotations for
       
   837      * CSS compatibility.  So, enforce the length. */
       
   838      if (*pp - p != 4)
       
   839        return false;
       
   840     if (*pp == end || **pp != quote)
       
   841       return false;
       
   842     (*pp)++;
       
   843   }
       
   844 
       
   845   return true;
       
   846 }
       
   847 
       
   848 static bool
       
   849 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
       
   850 {
       
   851   parse_space (pp, end);
       
   852 
       
   853   bool has_start;
       
   854 
       
   855   feature->start = 0;
       
   856   feature->end = (unsigned int) -1;
       
   857 
       
   858   if (!parse_char (pp, end, '['))
       
   859     return true;
       
   860 
       
   861   has_start = parse_uint (pp, end, &feature->start);
       
   862 
       
   863   if (parse_char (pp, end, ':')) {
       
   864     parse_uint (pp, end, &feature->end);
       
   865   } else {
       
   866     if (has_start)
       
   867       feature->end = feature->start + 1;
       
   868   }
       
   869 
       
   870   return parse_char (pp, end, ']');
       
   871 }
       
   872 
       
   873 static bool
       
   874 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
       
   875 {
       
   876   bool had_equal = parse_char (pp, end, '=');
       
   877   bool had_value = parse_uint32 (pp, end, &feature->value) ||
       
   878                    parse_bool (pp, end, &feature->value);
       
   879   /* CSS doesn't use equal-sign between tag and value.
       
   880    * If there was an equal-sign, then there *must* be a value.
       
   881    * A value without an eqaul-sign is ok, but not required. */
       
   882   return !had_equal || had_value;
       
   883 }
       
   884 
       
   885 static bool
       
   886 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
       
   887 {
       
   888   return parse_feature_value_prefix (pp, end, feature) &&
       
   889          parse_tag (pp, end, &feature->tag) &&
       
   890          parse_feature_indices (pp, end, feature) &&
       
   891          parse_feature_value_postfix (pp, end, feature) &&
       
   892          parse_space (pp, end) &&
       
   893          *pp == end;
       
   894 }
       
   895 
       
   896 /**
       
   897  * hb_feature_from_string:
       
   898  * @str: (array length=len) (element-type uint8_t): a string to parse
       
   899  * @len: length of @str, or -1 if string is %NULL terminated
       
   900  * @feature: (out): the #hb_feature_t to initialize with the parsed values
       
   901  *
       
   902  * Parses a string into a #hb_feature_t.
       
   903  *
       
   904  * TODO: document the syntax here.
       
   905  *
       
   906  * Return value:
       
   907  * %true if @str is successfully parsed, %false otherwise.
       
   908  *
       
   909  * Since: 0.9.5
       
   910  **/
       
   911 hb_bool_t
       
   912 hb_feature_from_string (const char *str, int len,
       
   913                         hb_feature_t *feature)
       
   914 {
       
   915   hb_feature_t feat;
       
   916 
       
   917   if (len < 0)
       
   918     len = strlen (str);
       
   919 
       
   920   if (likely (parse_one_feature (&str, str + len, &feat)))
       
   921   {
       
   922     if (feature)
       
   923       *feature = feat;
       
   924     return true;
       
   925   }
       
   926 
       
   927   if (feature)
       
   928     memset (feature, 0, sizeof (*feature));
       
   929   return false;
       
   930 }
       
   931 
       
   932 /**
       
   933  * hb_feature_to_string:
       
   934  * @feature: an #hb_feature_t to convert
       
   935  * @buf: (array length=size) (out): output string
       
   936  * @size: the allocated size of @buf
       
   937  *
       
   938  * Converts a #hb_feature_t into a %NULL-terminated string in the format
       
   939  * understood by hb_feature_from_string(). The client in responsible for
       
   940  * allocating big enough size for @buf, 128 bytes is more than enough.
       
   941  *
       
   942  * Since: 0.9.5
       
   943  **/
       
   944 void
       
   945 hb_feature_to_string (hb_feature_t *feature,
       
   946                       char *buf, unsigned int size)
       
   947 {
       
   948   if (unlikely (!size)) return;
       
   949 
       
   950   char s[128];
       
   951   unsigned int len = 0;
       
   952   if (feature->value == 0)
       
   953     s[len++] = '-';
       
   954   hb_tag_to_string (feature->tag, s + len);
       
   955   len += 4;
       
   956   while (len && s[len - 1] == ' ')
       
   957     len--;
       
   958   if (feature->start != 0 || feature->end != (unsigned int) -1)
       
   959   {
       
   960     s[len++] = '[';
       
   961     if (feature->start)
       
   962       len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
       
   963     if (feature->end != feature->start + 1) {
       
   964       s[len++] = ':';
       
   965       if (feature->end != (unsigned int) -1)
       
   966         len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
       
   967     }
       
   968     s[len++] = ']';
       
   969   }
       
   970   if (feature->value > 1)
       
   971   {
       
   972     s[len++] = '=';
       
   973     len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
       
   974   }
       
   975   assert (len < ARRAY_LENGTH (s));
       
   976   len = MIN (len, size - 1);
       
   977   memcpy (buf, s, len);
       
   978   buf[len] = '\0';
       
   979 }
       
   980 
       
   981 /* hb_variation_t */
       
   982 
       
   983 static bool
       
   984 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
       
   985 {
       
   986   parse_char (pp, end, '='); /* Optional. */
       
   987   return parse_float (pp, end, &variation->value);
       
   988 }
       
   989 
       
   990 static bool
       
   991 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
       
   992 {
       
   993   return parse_tag (pp, end, &variation->tag) &&
       
   994          parse_variation_value (pp, end, variation) &&
       
   995          parse_space (pp, end) &&
       
   996          *pp == end;
       
   997 }
       
   998 
       
   999 /**
       
  1000  * hb_variation_from_string:
       
  1001  *
       
  1002  * Since: 1.4.2
       
  1003  */
       
  1004 hb_bool_t
       
  1005 hb_variation_from_string (const char *str, int len,
       
  1006                           hb_variation_t *variation)
       
  1007 {
       
  1008   hb_variation_t var;
       
  1009 
       
  1010   if (len < 0)
       
  1011     len = strlen (str);
       
  1012 
       
  1013   if (likely (parse_one_variation (&str, str + len, &var)))
       
  1014   {
       
  1015     if (variation)
       
  1016       *variation = var;
       
  1017     return true;
       
  1018   }
       
  1019 
       
  1020   if (variation)
       
  1021     memset (variation, 0, sizeof (*variation));
       
  1022   return false;
       
  1023 }
       
  1024 
       
  1025 /**
       
  1026  * hb_variation_to_string:
       
  1027  *
       
  1028  * Since: 1.4.2
       
  1029  */
       
  1030 void
       
  1031 hb_variation_to_string (hb_variation_t *variation,
       
  1032                         char *buf, unsigned int size)
       
  1033 {
       
  1034   if (unlikely (!size)) return;
       
  1035 
       
  1036   char s[128];
       
  1037   unsigned int len = 0;
       
  1038   hb_tag_to_string (variation->tag, s + len);
       
  1039   len += 4;
       
  1040   while (len && s[len - 1] == ' ')
       
  1041     len--;
       
  1042   s[len++] = '=';
       
  1043   len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
       
  1044 
       
  1045   assert (len < ARRAY_LENGTH (s));
       
  1046   len = MIN (len, size - 1);
       
  1047   memcpy (buf, s, len);
       
  1048   buf[len] = '\0';
       
  1049 }