Path: blob/master/src/java.desktop/share/native/libharfbuzz/hb-common.cc
41149 views
/*1* Copyright © 2009,2010 Red Hat, Inc.2* Copyright © 2011,2012 Google, Inc.3*4* This is part of HarfBuzz, a text shaping library.5*6* Permission is hereby granted, without written agreement and without7* license or royalty fees, to use, copy, modify, and distribute this8* software and its documentation for any purpose, provided that the9* above copyright notice and the following two paragraphs appear in10* all copies of this software.11*12* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR13* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES14* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN15* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH16* DAMAGE.17*18* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,19* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND20* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS21* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO22* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.23*24* Red Hat Author(s): Behdad Esfahbod25* Google Author(s): Behdad Esfahbod26*/2728#include "hb.hh"29#include "hb-machinery.hh"3031#include <locale.h>3233#ifdef HB_NO_SETLOCALE34#define setlocale(Category, Locale) "C"35#endif3637/**38* SECTION:hb-common39* @title: hb-common40* @short_description: Common data types41* @include: hb.h42*43* Common data types used across HarfBuzz are defined here.44**/454647/* hb_options_t */4849hb_atomic_int_t _hb_options;5051void52_hb_options_init ()53{54hb_options_union_t u;55u.i = 0;56u.opts.initialized = true;5758const char *c = getenv ("HB_OPTIONS");59if (c)60{61while (*c)62{63const char *p = strchr (c, ':');64if (!p)65p = c + strlen (c);6667#define OPTION(name, symbol) \68if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0)6970OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);7172#undef OPTION7374c = *p ? p + 1 : p;75}7677}7879/* This is idempotent and threadsafe. */80_hb_options.set_relaxed (u.i);81}828384/* hb_tag_t */8586/**87* hb_tag_from_string:88* @str: (array length=len) (element-type uint8_t): String to convert89* @len: Length of @str, or -1 if it is %NULL-terminated90*91* Converts a string into an #hb_tag_t. Valid tags92* are four characters. Shorter input strings will be93* padded with spaces. Longer input strings will be94* truncated.95*96* Return value: The #hb_tag_t corresponding to @str97*98* Since: 0.9.299**/100hb_tag_t101hb_tag_from_string (const char *str, int len)102{103char tag[4];104unsigned int i;105106if (!str || !len || !*str)107return HB_TAG_NONE;108109if (len < 0 || len > 4)110len = 4;111for (i = 0; i < (unsigned) len && str[i]; i++)112tag[i] = str[i];113for (; i < 4; i++)114tag[i] = ' ';115116return HB_TAG (tag[0], tag[1], tag[2], tag[3]);117}118119/**120* hb_tag_to_string:121* @tag: #hb_tag_t to convert122* @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string123*124* Converts an #hb_tag_t to a string and returns it in @buf.125* Strings will be four characters long.126*127* Since: 0.9.5128**/129void130hb_tag_to_string (hb_tag_t tag, char *buf)131{132buf[0] = (char) (uint8_t) (tag >> 24);133buf[1] = (char) (uint8_t) (tag >> 16);134buf[2] = (char) (uint8_t) (tag >> 8);135buf[3] = (char) (uint8_t) (tag >> 0);136}137138139/* hb_direction_t */140141const char direction_strings[][4] = {142"ltr",143"rtl",144"ttb",145"btt"146};147148/**149* hb_direction_from_string:150* @str: (array length=len) (element-type uint8_t): String to convert151* @len: Length of @str, or -1 if it is %NULL-terminated152*153* Converts a string to an #hb_direction_t.154*155* Matching is loose and applies only to the first letter. For156* examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.157*158* Unmatched strings will return #HB_DIRECTION_INVALID.159*160* Return value: The #hb_direction_t matching @str161*162* Since: 0.9.2163**/164hb_direction_t165hb_direction_from_string (const char *str, int len)166{167if (unlikely (!str || !len || !*str))168return HB_DIRECTION_INVALID;169170/* Lets match loosely: just match the first letter, such that171* all of "ltr", "left-to-right", etc work!172*/173char c = TOLOWER (str[0]);174for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)175if (c == direction_strings[i][0])176return (hb_direction_t) (HB_DIRECTION_LTR + i);177178return HB_DIRECTION_INVALID;179}180181/**182* hb_direction_to_string:183* @direction: The #hb_direction_t to convert184*185* Converts an #hb_direction_t to a string.186*187* Return value: (transfer none): The string corresponding to @direction188*189* Since: 0.9.2190**/191const char *192hb_direction_to_string (hb_direction_t direction)193{194if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)195< ARRAY_LENGTH (direction_strings)))196return direction_strings[direction - HB_DIRECTION_LTR];197198return "invalid";199}200201202/* hb_language_t */203204struct hb_language_impl_t {205const char s[1];206};207208static const char canon_map[256] = {2090, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,2100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,2110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,212'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,2130, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',214'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',2150, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',216'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0217};218219static bool220lang_equal (hb_language_t v1,221const void *v2)222{223const unsigned char *p1 = (const unsigned char *) v1;224const unsigned char *p2 = (const unsigned char *) v2;225226while (*p1 && *p1 == canon_map[*p2]) {227p1++;228p2++;229}230231return *p1 == canon_map[*p2];232}233234#if 0235static unsigned int236lang_hash (const void *key)237{238const unsigned char *p = key;239unsigned int h = 0;240while (canon_map[*p])241{242h = (h << 5) - h + canon_map[*p];243p++;244}245246return h;247}248#endif249250251struct hb_language_item_t {252253struct hb_language_item_t *next;254hb_language_t lang;255256bool operator == (const char *s) const257{ return lang_equal (lang, s); }258259hb_language_item_t & operator = (const char *s) {260/* If a custom allocated is used calling strdup() pairs261badly with a call to the custom free() in fini() below.262Therefore don't call strdup(), implement its behavior.263*/264size_t len = strlen(s) + 1;265lang = (hb_language_t) malloc(len);266if (likely (lang))267{268memcpy((unsigned char *) lang, s, len);269for (unsigned char *p = (unsigned char *) lang; *p; p++)270*p = canon_map[*p];271}272273return *this;274}275276void fini () { free ((void *) lang); }277};278279280/* Thread-safe lock-free language list */281282static hb_atomic_ptr_t <hb_language_item_t> langs;283284#if HB_USE_ATEXIT285static void286free_langs ()287{288retry:289hb_language_item_t *first_lang = langs;290if (unlikely (!langs.cmpexch (first_lang, nullptr)))291goto retry;292293while (first_lang) {294hb_language_item_t *next = first_lang->next;295first_lang->fini ();296free (first_lang);297first_lang = next;298}299}300#endif301302static hb_language_item_t *303lang_find_or_insert (const char *key)304{305retry:306hb_language_item_t *first_lang = langs;307308for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)309if (*lang == key)310return lang;311312/* Not found; allocate one. */313hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));314if (unlikely (!lang))315return nullptr;316lang->next = first_lang;317*lang = key;318if (unlikely (!lang->lang))319{320free (lang);321return nullptr;322}323324if (unlikely (!langs.cmpexch (first_lang, lang)))325{326lang->fini ();327free (lang);328goto retry;329}330331#if HB_USE_ATEXIT332if (!first_lang)333atexit (free_langs); /* First person registers atexit() callback. */334#endif335336return lang;337}338339340/**341* hb_language_from_string:342* @str: (array length=len) (element-type uint8_t): a string representing343* a BCP 47 language tag344* @len: length of the @str, or -1 if it is %NULL-terminated.345*346* Converts @str representing a BCP 47 language tag to the corresponding347* #hb_language_t.348*349* Return value: (transfer none):350* The #hb_language_t corresponding to the BCP 47 language tag.351*352* Since: 0.9.2353**/354hb_language_t355hb_language_from_string (const char *str, int len)356{357if (!str || !len || !*str)358return HB_LANGUAGE_INVALID;359360hb_language_item_t *item = nullptr;361if (len >= 0)362{363/* NUL-terminate it. */364char strbuf[64];365len = hb_min (len, (int) sizeof (strbuf) - 1);366memcpy (strbuf, str, len);367strbuf[len] = '\0';368item = lang_find_or_insert (strbuf);369}370else371item = lang_find_or_insert (str);372373return likely (item) ? item->lang : HB_LANGUAGE_INVALID;374}375376/**377* hb_language_to_string:378* @language: The #hb_language_t to convert379*380* Converts an #hb_language_t to a string.381*382* Return value: (transfer none):383* A %NULL-terminated string representing the @language. Must not be freed by384* the caller.385*386* Since: 0.9.2387**/388const char *389hb_language_to_string (hb_language_t language)390{391if (unlikely (!language)) return nullptr;392393return language->s;394}395396/**397* hb_language_get_default:398*399* Fetch the default language from current locale.400*401* <note>Note that the first time this function is called, it calls402* "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying403* setlocale function is, in many implementations, NOT threadsafe. To avoid404* problems, call this function once before multiple threads can call it.405* This function is only used from hb_buffer_guess_segment_properties() by406* HarfBuzz itself.</note>407*408* Return value: (transfer none): The default language of the locale as409* an #hb_language_t410*411* Since: 0.9.2412**/413hb_language_t414hb_language_get_default ()415{416static hb_atomic_ptr_t <hb_language_t> default_language;417418hb_language_t language = default_language;419if (unlikely (language == HB_LANGUAGE_INVALID))420{421language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);422(void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);423}424425return language;426}427428429/* hb_script_t */430431/**432* hb_script_from_iso15924_tag:433* @tag: an #hb_tag_t representing an ISO 15924 tag.434*435* Converts an ISO 15924 script tag to a corresponding #hb_script_t.436*437* Return value:438* An #hb_script_t corresponding to the ISO 15924 tag.439*440* Since: 0.9.2441**/442hb_script_t443hb_script_from_iso15924_tag (hb_tag_t tag)444{445if (unlikely (tag == HB_TAG_NONE))446return HB_SCRIPT_INVALID;447448/* Be lenient, adjust case (one capital letter followed by three small letters) */449tag = (tag & 0xDFDFDFDFu) | 0x00202020u;450451switch (tag) {452453/* These graduated from the 'Q' private-area codes, but454* the old code is still aliased by Unicode, and the Qaai455* one in use by ICU. */456case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;457case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;458459/* Script variants from https://unicode.org/iso15924/ */460case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;461case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;462case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;463case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;464case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;465case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;466case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;467case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;468case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;469case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;470case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;471}472473/* If it looks right, just use the tag as a script */474if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)475return (hb_script_t) tag;476477/* Otherwise, return unknown */478return HB_SCRIPT_UNKNOWN;479}480481/**482* hb_script_from_string:483* @str: (array length=len) (element-type uint8_t): a string representing an484* ISO 15924 tag.485* @len: length of the @str, or -1 if it is %NULL-terminated.486*487* Converts a string @str representing an ISO 15924 script tag to a488* corresponding #hb_script_t. Shorthand for hb_tag_from_string() then489* hb_script_from_iso15924_tag().490*491* Return value:492* An #hb_script_t corresponding to the ISO 15924 tag.493*494* Since: 0.9.2495**/496hb_script_t497hb_script_from_string (const char *str, int len)498{499return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));500}501502/**503* hb_script_to_iso15924_tag:504* @script: an #hb_script_t to convert.505*506* Converts an #hb_script_t to a corresponding ISO 15924 script tag.507*508* Return value:509* An #hb_tag_t representing an ISO 15924 script tag.510*511* Since: 0.9.2512**/513hb_tag_t514hb_script_to_iso15924_tag (hb_script_t script)515{516return (hb_tag_t) script;517}518519/**520* hb_script_get_horizontal_direction:521* @script: The #hb_script_t to query522*523* Fetches the #hb_direction_t of a script when it is524* set horizontally. All right-to-left scripts will return525* #HB_DIRECTION_RTL. All left-to-right scripts will return526* #HB_DIRECTION_LTR. Scripts that can be written either527* horizontally or vertically will return #HB_DIRECTION_INVALID.528* Unknown scripts will return #HB_DIRECTION_LTR.529*530* Return value: The horizontal #hb_direction_t of @script531*532* Since: 0.9.2533**/534hb_direction_t535hb_script_get_horizontal_direction (hb_script_t script)536{537/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */538switch ((hb_tag_t) script)539{540/* Unicode-1.1 additions */541case HB_SCRIPT_ARABIC:542case HB_SCRIPT_HEBREW:543544/* Unicode-3.0 additions */545case HB_SCRIPT_SYRIAC:546case HB_SCRIPT_THAANA:547548/* Unicode-4.0 additions */549case HB_SCRIPT_CYPRIOT:550551/* Unicode-4.1 additions */552case HB_SCRIPT_KHAROSHTHI:553554/* Unicode-5.0 additions */555case HB_SCRIPT_PHOENICIAN:556case HB_SCRIPT_NKO:557558/* Unicode-5.1 additions */559case HB_SCRIPT_LYDIAN:560561/* Unicode-5.2 additions */562case HB_SCRIPT_AVESTAN:563case HB_SCRIPT_IMPERIAL_ARAMAIC:564case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:565case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:566case HB_SCRIPT_OLD_SOUTH_ARABIAN:567case HB_SCRIPT_OLD_TURKIC:568case HB_SCRIPT_SAMARITAN:569570/* Unicode-6.0 additions */571case HB_SCRIPT_MANDAIC:572573/* Unicode-6.1 additions */574case HB_SCRIPT_MEROITIC_CURSIVE:575case HB_SCRIPT_MEROITIC_HIEROGLYPHS:576577/* Unicode-7.0 additions */578case HB_SCRIPT_MANICHAEAN:579case HB_SCRIPT_MENDE_KIKAKUI:580case HB_SCRIPT_NABATAEAN:581case HB_SCRIPT_OLD_NORTH_ARABIAN:582case HB_SCRIPT_PALMYRENE:583case HB_SCRIPT_PSALTER_PAHLAVI:584585/* Unicode-8.0 additions */586case HB_SCRIPT_HATRAN:587588/* Unicode-9.0 additions */589case HB_SCRIPT_ADLAM:590591/* Unicode-11.0 additions */592case HB_SCRIPT_HANIFI_ROHINGYA:593case HB_SCRIPT_OLD_SOGDIAN:594case HB_SCRIPT_SOGDIAN:595596/* Unicode-12.0 additions */597case HB_SCRIPT_ELYMAIC:598599/* Unicode-13.0 additions */600case HB_SCRIPT_CHORASMIAN:601case HB_SCRIPT_YEZIDI:602603return HB_DIRECTION_RTL;604605606/* https://github.com/harfbuzz/harfbuzz/issues/1000 */607case HB_SCRIPT_OLD_HUNGARIAN:608case HB_SCRIPT_OLD_ITALIC:609case HB_SCRIPT_RUNIC:610611return HB_DIRECTION_INVALID;612}613614return HB_DIRECTION_LTR;615}616617618/* hb_version */619620621/**622* SECTION:hb-version623* @title: hb-version624* @short_description: Information about the version of HarfBuzz in use625* @include: hb.h626*627* These functions and macros allow accessing version of the HarfBuzz628* library used at compile- as well as run-time, and to direct code629* conditionally based on those versions, again, at compile- or run-time.630**/631632633/**634* hb_version:635* @major: (out): Library major version component636* @minor: (out): Library minor version component637* @micro: (out): Library micro version component638*639* Returns library version as three integer components.640*641* Since: 0.9.2642**/643void644hb_version (unsigned int *major,645unsigned int *minor,646unsigned int *micro)647{648*major = HB_VERSION_MAJOR;649*minor = HB_VERSION_MINOR;650*micro = HB_VERSION_MICRO;651}652653/**654* hb_version_string:655*656* Returns library version as a string with three components.657*658* Return value: Library version string659*660* Since: 0.9.2661**/662const char *663hb_version_string ()664{665return HB_VERSION_STRING;666}667668/**669* hb_version_atleast:670* @major: Library major version component671* @minor: Library minor version component672* @micro: Library micro version component673*674* Tests the library version against a minimum value,675* as three integer components.676*677* Return value: %true if the library is equal to or greater than678* the test value, %false otherwise679*680* Since: 0.9.30681**/682hb_bool_t683hb_version_atleast (unsigned int major,684unsigned int minor,685unsigned int micro)686{687return HB_VERSION_ATLEAST (major, minor, micro);688}689690691692/* hb_feature_t and hb_variation_t */693694static bool695parse_space (const char **pp, const char *end)696{697while (*pp < end && ISSPACE (**pp))698(*pp)++;699return true;700}701702static bool703parse_char (const char **pp, const char *end, char c)704{705parse_space (pp, end);706707if (*pp == end || **pp != c)708return false;709710(*pp)++;711return true;712}713714static bool715parse_uint (const char **pp, const char *end, unsigned int *pv)716{717/* Intentionally use hb_parse_int inside instead of hb_parse_uint,718* such that -1 turns into "big number"... */719int v;720if (unlikely (!hb_parse_int (pp, end, &v))) return false;721722*pv = v;723return true;724}725726static bool727parse_uint32 (const char **pp, const char *end, uint32_t *pv)728{729/* Intentionally use hb_parse_int inside instead of hb_parse_uint,730* such that -1 turns into "big number"... */731int v;732if (unlikely (!hb_parse_int (pp, end, &v))) return false;733734*pv = v;735return true;736}737738static bool739parse_bool (const char **pp, const char *end, uint32_t *pv)740{741parse_space (pp, end);742743const char *p = *pp;744while (*pp < end && ISALPHA(**pp))745(*pp)++;746747/* CSS allows on/off as aliases 1/0. */748if (*pp - p == 2749&& TOLOWER (p[0]) == 'o'750&& TOLOWER (p[1]) == 'n')751*pv = 1;752else if (*pp - p == 3753&& TOLOWER (p[0]) == 'o'754&& TOLOWER (p[1]) == 'f'755&& TOLOWER (p[2]) == 'f')756*pv = 0;757else758return false;759760return true;761}762763/* hb_feature_t */764765static bool766parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)767{768if (parse_char (pp, end, '-'))769feature->value = 0;770else {771parse_char (pp, end, '+');772feature->value = 1;773}774775return true;776}777778static bool779parse_tag (const char **pp, const char *end, hb_tag_t *tag)780{781parse_space (pp, end);782783char quote = 0;784785if (*pp < end && (**pp == '\'' || **pp == '"'))786{787quote = **pp;788(*pp)++;789}790791const char *p = *pp;792while (*pp < end && (ISALNUM(**pp) || **pp == '_'))793(*pp)++;794795if (p == *pp || *pp - p > 4)796return false;797798*tag = hb_tag_from_string (p, *pp - p);799800if (quote)801{802/* CSS expects exactly four bytes. And we only allow quotations for803* CSS compatibility. So, enforce the length. */804if (*pp - p != 4)805return false;806if (*pp == end || **pp != quote)807return false;808(*pp)++;809}810811return true;812}813814static bool815parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)816{817parse_space (pp, end);818819bool has_start;820821feature->start = HB_FEATURE_GLOBAL_START;822feature->end = HB_FEATURE_GLOBAL_END;823824if (!parse_char (pp, end, '['))825return true;826827has_start = parse_uint (pp, end, &feature->start);828829if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {830parse_uint (pp, end, &feature->end);831} else {832if (has_start)833feature->end = feature->start + 1;834}835836return parse_char (pp, end, ']');837}838839static bool840parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)841{842bool had_equal = parse_char (pp, end, '=');843bool had_value = parse_uint32 (pp, end, &feature->value) ||844parse_bool (pp, end, &feature->value);845/* CSS doesn't use equal-sign between tag and value.846* If there was an equal-sign, then there *must* be a value.847* A value without an equal-sign is ok, but not required. */848return !had_equal || had_value;849}850851static bool852parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)853{854return parse_feature_value_prefix (pp, end, feature) &&855parse_tag (pp, end, &feature->tag) &&856parse_feature_indices (pp, end, feature) &&857parse_feature_value_postfix (pp, end, feature) &&858parse_space (pp, end) &&859*pp == end;860}861862/**863* hb_feature_from_string:864* @str: (array length=len) (element-type uint8_t): a string to parse865* @len: length of @str, or -1 if string is %NULL terminated866* @feature: (out): the #hb_feature_t to initialize with the parsed values867*868* Parses a string into a #hb_feature_t.869*870* The format for specifying feature strings follows. All valid CSS871* font-feature-settings values other than 'normal' and the global values are872* also accepted, though not documented below. CSS string escapes are not873* supported.874*875* The range indices refer to the positions between Unicode characters. The876* position before the first character is always 0.877*878* The format is Python-esque. Here is how it all works:879*880* <informaltable pgwide='1' align='left' frame='none'>881* <tgroup cols='5'>882* <thead>883* <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>884* </thead>885* <tbody>886* <row><entry>Setting value:</entry></row>887* <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>888* <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>889* <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>890* <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>891* <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>892* <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>893* <row><entry>Setting index:</entry></row>894* <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>895* <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>896* <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>897* <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>898* <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>899* <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>900* <row><entry>Mixing it all:</entry></row>901* <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>902* </tbody>903* </tgroup>904* </informaltable>905*906* Return value:907* %true if @str is successfully parsed, %false otherwise908*909* Since: 0.9.5910**/911hb_bool_t912hb_feature_from_string (const char *str, int len,913hb_feature_t *feature)914{915hb_feature_t feat;916917if (len < 0)918len = strlen (str);919920if (likely (parse_one_feature (&str, str + len, &feat)))921{922if (feature)923*feature = feat;924return true;925}926927if (feature)928memset (feature, 0, sizeof (*feature));929return false;930}931932/**933* hb_feature_to_string:934* @feature: an #hb_feature_t to convert935* @buf: (array length=size) (out): output string936* @size: the allocated size of @buf937*938* Converts a #hb_feature_t into a %NULL-terminated string in the format939* understood by hb_feature_from_string(). The client in responsible for940* allocating big enough size for @buf, 128 bytes is more than enough.941*942* Since: 0.9.5943**/944void945hb_feature_to_string (hb_feature_t *feature,946char *buf, unsigned int size)947{948if (unlikely (!size)) return;949950char s[128];951unsigned int len = 0;952if (feature->value == 0)953s[len++] = '-';954hb_tag_to_string (feature->tag, s + len);955len += 4;956while (len && s[len - 1] == ' ')957len--;958if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)959{960s[len++] = '[';961if (feature->start)962len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));963if (feature->end != feature->start + 1) {964s[len++] = ':';965if (feature->end != HB_FEATURE_GLOBAL_END)966len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));967}968s[len++] = ']';969}970if (feature->value > 1)971{972s[len++] = '=';973len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));974}975assert (len < ARRAY_LENGTH (s));976len = hb_min (len, size - 1);977memcpy (buf, s, len);978buf[len] = '\0';979}980981/* hb_variation_t */982983static bool984parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)985{986parse_char (pp, end, '='); /* Optional. */987double v;988if (unlikely (!hb_parse_double (pp, end, &v))) return false;989990variation->value = v;991return true;992}993994static bool995parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)996{997return parse_tag (pp, end, &variation->tag) &&998parse_variation_value (pp, end, variation) &&999parse_space (pp, end) &&1000*pp == end;1001}10021003/**1004* hb_variation_from_string:1005* @str: (array length=len) (element-type uint8_t): a string to parse1006* @len: length of @str, or -1 if string is %NULL terminated1007* @variation: (out): the #hb_variation_t to initialize with the parsed values1008*1009* Parses a string into a #hb_variation_t.1010*1011* The format for specifying variation settings follows. All valid CSS1012* font-variation-settings values other than 'normal' and 'inherited' are also1013* accepted, though, not documented below.1014*1015* The format is a tag, optionally followed by an equals sign, followed by a1016* number. For example `wght=500`, or `slnt=-7.5`.1017*1018* Return value:1019* %true if @str is successfully parsed, %false otherwise1020*1021* Since: 1.4.21022*/1023hb_bool_t1024hb_variation_from_string (const char *str, int len,1025hb_variation_t *variation)1026{1027hb_variation_t var;10281029if (len < 0)1030len = strlen (str);10311032if (likely (parse_one_variation (&str, str + len, &var)))1033{1034if (variation)1035*variation = var;1036return true;1037}10381039if (variation)1040memset (variation, 0, sizeof (*variation));1041return false;1042}10431044/**1045* hb_variation_to_string:1046* @variation: an #hb_variation_t to convert1047* @buf: (array length=size) (out): output string1048* @size: the allocated size of @buf1049*1050* Converts an #hb_variation_t into a %NULL-terminated string in the format1051* understood by hb_variation_from_string(). The client in responsible for1052* allocating big enough size for @buf, 128 bytes is more than enough.1053*1054* Since: 1.4.21055*/1056void1057hb_variation_to_string (hb_variation_t *variation,1058char *buf, unsigned int size)1059{1060if (unlikely (!size)) return;10611062char s[128];1063unsigned int len = 0;1064hb_tag_to_string (variation->tag, s + len);1065len += 4;1066while (len && s[len - 1] == ' ')1067len--;1068s[len++] = '=';1069len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));10701071assert (len < ARRAY_LENGTH (s));1072len = hb_min (len, size - 1);1073memcpy (buf, s, len);1074buf[len] = '\0';1075}10761077/**1078* hb_color_get_alpha:1079* @color: an #hb_color_t we are interested in its channels.1080*1081* Fetches the alpha channel of the given @color.1082*1083* Return value: Alpha channel value1084*1085* Since: 2.1.01086*/1087uint8_t1088(hb_color_get_alpha) (hb_color_t color)1089{1090return hb_color_get_alpha (color);1091}10921093/**1094* hb_color_get_red:1095* @color: an #hb_color_t we are interested in its channels.1096*1097* Fetches the red channel of the given @color.1098*1099* Return value: Red channel value1100*1101* Since: 2.1.01102*/1103uint8_t1104(hb_color_get_red) (hb_color_t color)1105{1106return hb_color_get_red (color);1107}11081109/**1110* hb_color_get_green:1111* @color: an #hb_color_t we are interested in its channels.1112*1113* Fetches the green channel of the given @color.1114*1115* Return value: Green channel value1116*1117* Since: 2.1.01118*/1119uint8_t1120(hb_color_get_green) (hb_color_t color)1121{1122return hb_color_get_green (color);1123}11241125/**1126* hb_color_get_blue:1127* @color: an #hb_color_t we are interested in its channels.1128*1129* Fetches the blue channel of the given @color.1130*1131* Return value: Blue channel value1132*1133* Since: 2.1.01134*/1135uint8_t1136(hb_color_get_blue) (hb_color_t color)1137{1138return hb_color_get_blue (color);1139}114011411142/* If there is no visibility control, then hb-static.cc will NOT1143* define anything. Instead, we get it to define one set in here1144* only, so only libharfbuzz.so defines them, not other libs. */1145#ifdef HB_NO_VISIBILITY1146#undef HB_NO_VISIBILITY1147#include "hb-static.cc"1148#define HB_NO_VISIBILITY 11149#endif115011511152