Path: blob/master/src/java.desktop/share/native/libharfbuzz/hb-common.cc
66644 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#if !defined(HB_NO_SETLOCALE) && (!defined(HAVE_NEWLOCALE) || !defined(HAVE_USELOCALE))32#define HB_NO_SETLOCALE 133#endif3435#ifndef HB_NO_SETLOCALE3637#include <locale.h>38#ifdef HAVE_XLOCALE_H39#include <xlocale.h> // Needed on BSD/OS X for uselocale40#endif4142#ifdef WIN3243#define hb_locale_t _locale_t44#else45#define hb_locale_t locale_t46#endif47#define hb_setlocale setlocale48#define hb_uselocale uselocale4950#else5152#define hb_locale_t void *53#define hb_setlocale(Category, Locale) "C"54#define hb_uselocale(Locale) ((hb_locale_t) 0)5556#endif5758/**59* SECTION:hb-common60* @title: hb-common61* @short_description: Common data types62* @include: hb.h63*64* Common data types used across HarfBuzz are defined here.65**/666768/* hb_options_t */6970hb_atomic_int_t _hb_options;7172void73_hb_options_init ()74{75hb_options_union_t u;76u.i = 0;77u.opts.initialized = true;7879const char *c = getenv ("HB_OPTIONS");80if (c)81{82while (*c)83{84const char *p = strchr (c, ':');85if (!p)86p = c + strlen (c);8788#define OPTION(name, symbol) \89if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0)9091OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);9293#undef OPTION9495c = *p ? p + 1 : p;96}9798}99100/* This is idempotent and threadsafe. */101_hb_options.set_relaxed (u.i);102}103104105/* hb_tag_t */106107/**108* hb_tag_from_string:109* @str: (array length=len) (element-type uint8_t): String to convert110* @len: Length of @str, or -1 if it is %NULL-terminated111*112* Converts a string into an #hb_tag_t. Valid tags113* are four characters. Shorter input strings will be114* padded with spaces. Longer input strings will be115* truncated.116*117* Return value: The #hb_tag_t corresponding to @str118*119* Since: 0.9.2120**/121hb_tag_t122hb_tag_from_string (const char *str, int len)123{124char tag[4];125unsigned int i;126127if (!str || !len || !*str)128return HB_TAG_NONE;129130if (len < 0 || len > 4)131len = 4;132for (i = 0; i < (unsigned) len && str[i]; i++)133tag[i] = str[i];134for (; i < 4; i++)135tag[i] = ' ';136137return HB_TAG (tag[0], tag[1], tag[2], tag[3]);138}139140/**141* hb_tag_to_string:142* @tag: #hb_tag_t to convert143* @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string144*145* Converts an #hb_tag_t to a string and returns it in @buf.146* Strings will be four characters long.147*148* Since: 0.9.5149**/150void151hb_tag_to_string (hb_tag_t tag, char *buf)152{153buf[0] = (char) (uint8_t) (tag >> 24);154buf[1] = (char) (uint8_t) (tag >> 16);155buf[2] = (char) (uint8_t) (tag >> 8);156buf[3] = (char) (uint8_t) (tag >> 0);157}158159160/* hb_direction_t */161162static const char direction_strings[][4] = {163"ltr",164"rtl",165"ttb",166"btt"167};168169/**170* hb_direction_from_string:171* @str: (array length=len) (element-type uint8_t): String to convert172* @len: Length of @str, or -1 if it is %NULL-terminated173*174* Converts a string to an #hb_direction_t.175*176* Matching is loose and applies only to the first letter. For177* examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.178*179* Unmatched strings will return #HB_DIRECTION_INVALID.180*181* Return value: The #hb_direction_t matching @str182*183* Since: 0.9.2184**/185hb_direction_t186hb_direction_from_string (const char *str, int len)187{188if (unlikely (!str || !len || !*str))189return HB_DIRECTION_INVALID;190191/* Lets match loosely: just match the first letter, such that192* all of "ltr", "left-to-right", etc work!193*/194char c = TOLOWER (str[0]);195for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)196if (c == direction_strings[i][0])197return (hb_direction_t) (HB_DIRECTION_LTR + i);198199return HB_DIRECTION_INVALID;200}201202/**203* hb_direction_to_string:204* @direction: The #hb_direction_t to convert205*206* Converts an #hb_direction_t to a string.207*208* Return value: (transfer none): The string corresponding to @direction209*210* Since: 0.9.2211**/212const char *213hb_direction_to_string (hb_direction_t direction)214{215if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)216< ARRAY_LENGTH (direction_strings)))217return direction_strings[direction - HB_DIRECTION_LTR];218219return "invalid";220}221222223/* hb_language_t */224225struct hb_language_impl_t {226const char s[1];227};228229static const char canon_map[256] = {2300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,2310, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,2320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,233'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,2340, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',235'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',2360, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',237'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0238};239240static bool241lang_equal (hb_language_t v1,242const void *v2)243{244const unsigned char *p1 = (const unsigned char *) v1;245const unsigned char *p2 = (const unsigned char *) v2;246247while (*p1 && *p1 == canon_map[*p2]) {248p1++;249p2++;250}251252return *p1 == canon_map[*p2];253}254255#if 0256static unsigned int257lang_hash (const void *key)258{259const unsigned char *p = key;260unsigned int h = 0;261while (canon_map[*p])262{263h = (h << 5) - h + canon_map[*p];264p++;265}266267return h;268}269#endif270271272struct hb_language_item_t {273274struct hb_language_item_t *next;275hb_language_t lang;276277bool operator == (const char *s) const278{ return lang_equal (lang, s); }279280hb_language_item_t & operator = (const char *s)281{282/* We can't call strdup(), because we allow custom allocators. */283size_t len = strlen(s) + 1;284lang = (hb_language_t) hb_malloc(len);285if (likely (lang))286{287memcpy((unsigned char *) lang, s, len);288for (unsigned char *p = (unsigned char *) lang; *p; p++)289*p = canon_map[*p];290}291292return *this;293}294295void fini () { hb_free ((void *) lang); }296};297298299/* Thread-safe lockfree language list */300301static hb_atomic_ptr_t <hb_language_item_t> langs;302303static inline void304free_langs ()305{306retry:307hb_language_item_t *first_lang = langs;308if (unlikely (!langs.cmpexch (first_lang, nullptr)))309goto retry;310311while (first_lang) {312hb_language_item_t *next = first_lang->next;313first_lang->fini ();314hb_free (first_lang);315first_lang = next;316}317}318319static hb_language_item_t *320lang_find_or_insert (const char *key)321{322retry:323hb_language_item_t *first_lang = langs;324325for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)326if (*lang == key)327return lang;328329/* Not found; allocate one. */330hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));331if (unlikely (!lang))332return nullptr;333lang->next = first_lang;334*lang = key;335if (unlikely (!lang->lang))336{337hb_free (lang);338return nullptr;339}340341if (unlikely (!langs.cmpexch (first_lang, lang)))342{343lang->fini ();344hb_free (lang);345goto retry;346}347348if (!first_lang)349hb_atexit (free_langs); /* First person registers atexit() callback. */350351return lang;352}353354355/**356* hb_language_from_string:357* @str: (array length=len) (element-type uint8_t): a string representing358* a BCP 47 language tag359* @len: length of the @str, or -1 if it is %NULL-terminated.360*361* Converts @str representing a BCP 47 language tag to the corresponding362* #hb_language_t.363*364* Return value: (transfer none):365* The #hb_language_t corresponding to the BCP 47 language tag.366*367* Since: 0.9.2368**/369hb_language_t370hb_language_from_string (const char *str, int len)371{372if (!str || !len || !*str)373return HB_LANGUAGE_INVALID;374375hb_language_item_t *item = nullptr;376if (len >= 0)377{378/* NUL-terminate it. */379char strbuf[64];380len = hb_min (len, (int) sizeof (strbuf) - 1);381memcpy (strbuf, str, len);382strbuf[len] = '\0';383item = lang_find_or_insert (strbuf);384}385else386item = lang_find_or_insert (str);387388return likely (item) ? item->lang : HB_LANGUAGE_INVALID;389}390391/**392* hb_language_to_string:393* @language: The #hb_language_t to convert394*395* Converts an #hb_language_t to a string.396*397* Return value: (transfer none):398* A %NULL-terminated string representing the @language. Must not be freed by399* the caller.400*401* Since: 0.9.2402**/403const char *404hb_language_to_string (hb_language_t language)405{406if (unlikely (!language)) return nullptr;407408return language->s;409}410411/**412* hb_language_get_default:413*414* Fetch the default language from current locale.415*416* <note>Note that the first time this function is called, it calls417* "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying418* setlocale function is, in many implementations, NOT threadsafe. To avoid419* problems, call this function once before multiple threads can call it.420* This function is only used from hb_buffer_guess_segment_properties() by421* HarfBuzz itself.</note>422*423* Return value: (transfer none): The default language of the locale as424* an #hb_language_t425*426* Since: 0.9.2427**/428hb_language_t429hb_language_get_default ()430{431static hb_atomic_ptr_t <hb_language_t> default_language;432433hb_language_t language = default_language;434if (unlikely (language == HB_LANGUAGE_INVALID))435{436language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1);437(void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);438}439440return language;441}442443444/* hb_script_t */445446/**447* hb_script_from_iso15924_tag:448* @tag: an #hb_tag_t representing an ISO 15924 tag.449*450* Converts an ISO 15924 script tag to a corresponding #hb_script_t.451*452* Return value:453* An #hb_script_t corresponding to the ISO 15924 tag.454*455* Since: 0.9.2456**/457hb_script_t458hb_script_from_iso15924_tag (hb_tag_t tag)459{460if (unlikely (tag == HB_TAG_NONE))461return HB_SCRIPT_INVALID;462463/* Be lenient, adjust case (one capital letter followed by three small letters) */464tag = (tag & 0xDFDFDFDFu) | 0x00202020u;465466switch (tag) {467468/* These graduated from the 'Q' private-area codes, but469* the old code is still aliased by Unicode, and the Qaai470* one in use by ICU. */471case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;472case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;473474/* Script variants from https://unicode.org/iso15924/ */475case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;476case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;477case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;478case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;479case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;480case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;481case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;482case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;483case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;484case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;485case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;486}487488/* If it looks right, just use the tag as a script */489if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)490return (hb_script_t) tag;491492/* Otherwise, return unknown */493return HB_SCRIPT_UNKNOWN;494}495496/**497* hb_script_from_string:498* @str: (array length=len) (element-type uint8_t): a string representing an499* ISO 15924 tag.500* @len: length of the @str, or -1 if it is %NULL-terminated.501*502* Converts a string @str representing an ISO 15924 script tag to a503* corresponding #hb_script_t. Shorthand for hb_tag_from_string() then504* hb_script_from_iso15924_tag().505*506* Return value:507* An #hb_script_t corresponding to the ISO 15924 tag.508*509* Since: 0.9.2510**/511hb_script_t512hb_script_from_string (const char *str, int len)513{514return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));515}516517/**518* hb_script_to_iso15924_tag:519* @script: an #hb_script_t to convert.520*521* Converts an #hb_script_t to a corresponding ISO 15924 script tag.522*523* Return value:524* An #hb_tag_t representing an ISO 15924 script tag.525*526* Since: 0.9.2527**/528hb_tag_t529hb_script_to_iso15924_tag (hb_script_t script)530{531return (hb_tag_t) script;532}533534/**535* hb_script_get_horizontal_direction:536* @script: The #hb_script_t to query537*538* Fetches the #hb_direction_t of a script when it is539* set horizontally. All right-to-left scripts will return540* #HB_DIRECTION_RTL. All left-to-right scripts will return541* #HB_DIRECTION_LTR. Scripts that can be written either542* horizontally or vertically will return #HB_DIRECTION_INVALID.543* Unknown scripts will return #HB_DIRECTION_LTR.544*545* Return value: The horizontal #hb_direction_t of @script546*547* Since: 0.9.2548**/549hb_direction_t550hb_script_get_horizontal_direction (hb_script_t script)551{552/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */553switch ((hb_tag_t) script)554{555/* Unicode-1.1 additions */556case HB_SCRIPT_ARABIC:557case HB_SCRIPT_HEBREW:558559/* Unicode-3.0 additions */560case HB_SCRIPT_SYRIAC:561case HB_SCRIPT_THAANA:562563/* Unicode-4.0 additions */564case HB_SCRIPT_CYPRIOT:565566/* Unicode-4.1 additions */567case HB_SCRIPT_KHAROSHTHI:568569/* Unicode-5.0 additions */570case HB_SCRIPT_PHOENICIAN:571case HB_SCRIPT_NKO:572573/* Unicode-5.1 additions */574case HB_SCRIPT_LYDIAN:575576/* Unicode-5.2 additions */577case HB_SCRIPT_AVESTAN:578case HB_SCRIPT_IMPERIAL_ARAMAIC:579case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:580case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:581case HB_SCRIPT_OLD_SOUTH_ARABIAN:582case HB_SCRIPT_OLD_TURKIC:583case HB_SCRIPT_SAMARITAN:584585/* Unicode-6.0 additions */586case HB_SCRIPT_MANDAIC:587588/* Unicode-6.1 additions */589case HB_SCRIPT_MEROITIC_CURSIVE:590case HB_SCRIPT_MEROITIC_HIEROGLYPHS:591592/* Unicode-7.0 additions */593case HB_SCRIPT_MANICHAEAN:594case HB_SCRIPT_MENDE_KIKAKUI:595case HB_SCRIPT_NABATAEAN:596case HB_SCRIPT_OLD_NORTH_ARABIAN:597case HB_SCRIPT_PALMYRENE:598case HB_SCRIPT_PSALTER_PAHLAVI:599600/* Unicode-8.0 additions */601case HB_SCRIPT_HATRAN:602603/* Unicode-9.0 additions */604case HB_SCRIPT_ADLAM:605606/* Unicode-11.0 additions */607case HB_SCRIPT_HANIFI_ROHINGYA:608case HB_SCRIPT_OLD_SOGDIAN:609case HB_SCRIPT_SOGDIAN:610611/* Unicode-12.0 additions */612case HB_SCRIPT_ELYMAIC:613614/* Unicode-13.0 additions */615case HB_SCRIPT_CHORASMIAN:616case HB_SCRIPT_YEZIDI:617618/* Unicode-14.0 additions */619case HB_SCRIPT_OLD_UYGHUR:620621return HB_DIRECTION_RTL;622623624/* https://github.com/harfbuzz/harfbuzz/issues/1000 */625case HB_SCRIPT_OLD_HUNGARIAN:626case HB_SCRIPT_OLD_ITALIC:627case HB_SCRIPT_RUNIC:628629return HB_DIRECTION_INVALID;630}631632return HB_DIRECTION_LTR;633}634635636/* hb_version */637638639/**640* SECTION:hb-version641* @title: hb-version642* @short_description: Information about the version of HarfBuzz in use643* @include: hb.h644*645* These functions and macros allow accessing version of the HarfBuzz646* library used at compile- as well as run-time, and to direct code647* conditionally based on those versions, again, at compile- or run-time.648**/649650651/**652* hb_version:653* @major: (out): Library major version component654* @minor: (out): Library minor version component655* @micro: (out): Library micro version component656*657* Returns library version as three integer components.658*659* Since: 0.9.2660**/661void662hb_version (unsigned int *major,663unsigned int *minor,664unsigned int *micro)665{666*major = HB_VERSION_MAJOR;667*minor = HB_VERSION_MINOR;668*micro = HB_VERSION_MICRO;669}670671/**672* hb_version_string:673*674* Returns library version as a string with three components.675*676* Return value: Library version string677*678* Since: 0.9.2679**/680const char *681hb_version_string ()682{683return HB_VERSION_STRING;684}685686/**687* hb_version_atleast:688* @major: Library major version component689* @minor: Library minor version component690* @micro: Library micro version component691*692* Tests the library version against a minimum value,693* as three integer components.694*695* Return value: %true if the library is equal to or greater than696* the test value, %false otherwise697*698* Since: 0.9.30699**/700hb_bool_t701hb_version_atleast (unsigned int major,702unsigned int minor,703unsigned int micro)704{705return HB_VERSION_ATLEAST (major, minor, micro);706}707708709710/* hb_feature_t and hb_variation_t */711712static bool713parse_space (const char **pp, const char *end)714{715while (*pp < end && ISSPACE (**pp))716(*pp)++;717return true;718}719720static bool721parse_char (const char **pp, const char *end, char c)722{723parse_space (pp, end);724725if (*pp == end || **pp != c)726return false;727728(*pp)++;729return true;730}731732static bool733parse_uint (const char **pp, const char *end, unsigned int *pv)734{735/* Intentionally use hb_parse_int inside instead of hb_parse_uint,736* such that -1 turns into "big number"... */737int v;738if (unlikely (!hb_parse_int (pp, end, &v))) return false;739740*pv = v;741return true;742}743744static bool745parse_uint32 (const char **pp, const char *end, uint32_t *pv)746{747/* Intentionally use hb_parse_int inside instead of hb_parse_uint,748* such that -1 turns into "big number"... */749int v;750if (unlikely (!hb_parse_int (pp, end, &v))) return false;751752*pv = v;753return true;754}755756static bool757parse_bool (const char **pp, const char *end, uint32_t *pv)758{759parse_space (pp, end);760761const char *p = *pp;762while (*pp < end && ISALPHA(**pp))763(*pp)++;764765/* CSS allows on/off as aliases 1/0. */766if (*pp - p == 2767&& TOLOWER (p[0]) == 'o'768&& TOLOWER (p[1]) == 'n')769*pv = 1;770else if (*pp - p == 3771&& TOLOWER (p[0]) == 'o'772&& TOLOWER (p[1]) == 'f'773&& TOLOWER (p[2]) == 'f')774*pv = 0;775else776return false;777778return true;779}780781/* hb_feature_t */782783static bool784parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)785{786if (parse_char (pp, end, '-'))787feature->value = 0;788else {789parse_char (pp, end, '+');790feature->value = 1;791}792793return true;794}795796static bool797parse_tag (const char **pp, const char *end, hb_tag_t *tag)798{799parse_space (pp, end);800801char quote = 0;802803if (*pp < end && (**pp == '\'' || **pp == '"'))804{805quote = **pp;806(*pp)++;807}808809const char *p = *pp;810while (*pp < end && (ISALNUM(**pp) || **pp == '_'))811(*pp)++;812813if (p == *pp || *pp - p > 4)814return false;815816*tag = hb_tag_from_string (p, *pp - p);817818if (quote)819{820/* CSS expects exactly four bytes. And we only allow quotations for821* CSS compatibility. So, enforce the length. */822if (*pp - p != 4)823return false;824if (*pp == end || **pp != quote)825return false;826(*pp)++;827}828829return true;830}831832static bool833parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)834{835parse_space (pp, end);836837bool has_start;838839feature->start = HB_FEATURE_GLOBAL_START;840feature->end = HB_FEATURE_GLOBAL_END;841842if (!parse_char (pp, end, '['))843return true;844845has_start = parse_uint (pp, end, &feature->start);846847if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {848parse_uint (pp, end, &feature->end);849} else {850if (has_start)851feature->end = feature->start + 1;852}853854return parse_char (pp, end, ']');855}856857static bool858parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)859{860bool had_equal = parse_char (pp, end, '=');861bool had_value = parse_uint32 (pp, end, &feature->value) ||862parse_bool (pp, end, &feature->value);863/* CSS doesn't use equal-sign between tag and value.864* If there was an equal-sign, then there *must* be a value.865* A value without an equal-sign is ok, but not required. */866return !had_equal || had_value;867}868869static bool870parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)871{872return parse_feature_value_prefix (pp, end, feature) &&873parse_tag (pp, end, &feature->tag) &&874parse_feature_indices (pp, end, feature) &&875parse_feature_value_postfix (pp, end, feature) &&876parse_space (pp, end) &&877*pp == end;878}879880/**881* hb_feature_from_string:882* @str: (array length=len) (element-type uint8_t): a string to parse883* @len: length of @str, or -1 if string is %NULL terminated884* @feature: (out): the #hb_feature_t to initialize with the parsed values885*886* Parses a string into a #hb_feature_t.887*888* The format for specifying feature strings follows. All valid CSS889* font-feature-settings values other than 'normal' and the global values are890* also accepted, though not documented below. CSS string escapes are not891* supported.892*893* The range indices refer to the positions between Unicode characters. The894* position before the first character is always 0.895*896* The format is Python-esque. Here is how it all works:897*898* <informaltable pgwide='1' align='left' frame='none'>899* <tgroup cols='5'>900* <thead>901* <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>902* </thead>903* <tbody>904* <row><entry>Setting value:</entry></row>905* <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>906* <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>907* <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>908* <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>909* <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>910* <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>911* <row><entry>Setting index:</entry></row>912* <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>913* <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>914* <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>915* <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>916* <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>917* <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>918* <row><entry>Mixing it all:</entry></row>919* <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>920* </tbody>921* </tgroup>922* </informaltable>923*924* Return value:925* %true if @str is successfully parsed, %false otherwise926*927* Since: 0.9.5928**/929hb_bool_t930hb_feature_from_string (const char *str, int len,931hb_feature_t *feature)932{933hb_feature_t feat;934935if (len < 0)936len = strlen (str);937938if (likely (parse_one_feature (&str, str + len, &feat)))939{940if (feature)941*feature = feat;942return true;943}944945if (feature)946memset (feature, 0, sizeof (*feature));947return false;948}949950/**951* hb_feature_to_string:952* @feature: an #hb_feature_t to convert953* @buf: (array length=size) (out): output string954* @size: the allocated size of @buf955*956* Converts a #hb_feature_t into a %NULL-terminated string in the format957* understood by hb_feature_from_string(). The client in responsible for958* allocating big enough size for @buf, 128 bytes is more than enough.959*960* Since: 0.9.5961**/962void963hb_feature_to_string (hb_feature_t *feature,964char *buf, unsigned int size)965{966if (unlikely (!size)) return;967968char s[128];969unsigned int len = 0;970if (feature->value == 0)971s[len++] = '-';972hb_tag_to_string (feature->tag, s + len);973len += 4;974while (len && s[len - 1] == ' ')975len--;976if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)977{978s[len++] = '[';979if (feature->start)980len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));981if (feature->end != feature->start + 1) {982s[len++] = ':';983if (feature->end != HB_FEATURE_GLOBAL_END)984len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));985}986s[len++] = ']';987}988if (feature->value > 1)989{990s[len++] = '=';991len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));992}993assert (len < ARRAY_LENGTH (s));994len = hb_min (len, size - 1);995memcpy (buf, s, len);996buf[len] = '\0';997}998999/* hb_variation_t */10001001static bool1002parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)1003{1004parse_char (pp, end, '='); /* Optional. */1005double v;1006if (unlikely (!hb_parse_double (pp, end, &v))) return false;10071008variation->value = v;1009return true;1010}10111012static bool1013parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)1014{1015return parse_tag (pp, end, &variation->tag) &&1016parse_variation_value (pp, end, variation) &&1017parse_space (pp, end) &&1018*pp == end;1019}10201021/**1022* hb_variation_from_string:1023* @str: (array length=len) (element-type uint8_t): a string to parse1024* @len: length of @str, or -1 if string is %NULL terminated1025* @variation: (out): the #hb_variation_t to initialize with the parsed values1026*1027* Parses a string into a #hb_variation_t.1028*1029* The format for specifying variation settings follows. All valid CSS1030* font-variation-settings values other than 'normal' and 'inherited' are also1031* accepted, though, not documented below.1032*1033* The format is a tag, optionally followed by an equals sign, followed by a1034* number. For example `wght=500`, or `slnt=-7.5`.1035*1036* Return value:1037* %true if @str is successfully parsed, %false otherwise1038*1039* Since: 1.4.21040*/1041hb_bool_t1042hb_variation_from_string (const char *str, int len,1043hb_variation_t *variation)1044{1045hb_variation_t var;10461047if (len < 0)1048len = strlen (str);10491050if (likely (parse_one_variation (&str, str + len, &var)))1051{1052if (variation)1053*variation = var;1054return true;1055}10561057if (variation)1058memset (variation, 0, sizeof (*variation));1059return false;1060}10611062#ifndef HB_NO_SETLOCALE10631064static inline void free_static_C_locale ();10651066static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,1067hb_C_locale_lazy_loader_t>1068{1069static hb_locale_t create ()1070{1071hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);1072if (!l)1073return l;10741075hb_atexit (free_static_C_locale);10761077return l;1078}1079static void destroy (hb_locale_t l)1080{1081freelocale (l);1082}1083static hb_locale_t get_null ()1084{1085return (hb_locale_t) 0;1086}1087} static_C_locale;10881089static inline1090void free_static_C_locale ()1091{1092static_C_locale.free_instance ();1093}10941095static hb_locale_t1096get_C_locale ()1097{1098return static_C_locale.get_unconst ();1099}11001101#endif11021103/**1104* hb_variation_to_string:1105* @variation: an #hb_variation_t to convert1106* @buf: (array length=size) (out): output string1107* @size: the allocated size of @buf1108*1109* Converts an #hb_variation_t into a %NULL-terminated string in the format1110* understood by hb_variation_from_string(). The client in responsible for1111* allocating big enough size for @buf, 128 bytes is more than enough.1112*1113* Since: 1.4.21114*/1115void1116hb_variation_to_string (hb_variation_t *variation,1117char *buf, unsigned int size)1118{1119if (unlikely (!size)) return;11201121char s[128];1122unsigned int len = 0;1123hb_tag_to_string (variation->tag, s + len);1124len += 4;1125while (len && s[len - 1] == ' ')1126len--;1127s[len++] = '=';11281129hb_locale_t oldlocale HB_UNUSED;1130oldlocale = hb_uselocale (get_C_locale ());1131len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));1132(void) hb_uselocale (oldlocale);11331134assert (len < ARRAY_LENGTH (s));1135len = hb_min (len, size - 1);1136memcpy (buf, s, len);1137buf[len] = '\0';1138}11391140/**1141* hb_color_get_alpha:1142* @color: an #hb_color_t we are interested in its channels.1143*1144* Fetches the alpha channel of the given @color.1145*1146* Return value: Alpha channel value1147*1148* Since: 2.1.01149*/1150uint8_t1151(hb_color_get_alpha) (hb_color_t color)1152{1153return hb_color_get_alpha (color);1154}11551156/**1157* hb_color_get_red:1158* @color: an #hb_color_t we are interested in its channels.1159*1160* Fetches the red channel of the given @color.1161*1162* Return value: Red channel value1163*1164* Since: 2.1.01165*/1166uint8_t1167(hb_color_get_red) (hb_color_t color)1168{1169return hb_color_get_red (color);1170}11711172/**1173* hb_color_get_green:1174* @color: an #hb_color_t we are interested in its channels.1175*1176* Fetches the green channel of the given @color.1177*1178* Return value: Green channel value1179*1180* Since: 2.1.01181*/1182uint8_t1183(hb_color_get_green) (hb_color_t color)1184{1185return hb_color_get_green (color);1186}11871188/**1189* hb_color_get_blue:1190* @color: an #hb_color_t we are interested in its channels.1191*1192* Fetches the blue channel of the given @color.1193*1194* Return value: Blue channel value1195*1196* Since: 2.1.01197*/1198uint8_t1199(hb_color_get_blue) (hb_color_t color)1200{1201return hb_color_get_blue (color);1202}120312041205/* If there is no visibility control, then hb-static.cc will NOT1206* define anything. Instead, we get it to define one set in here1207* only, so only libharfbuzz.so defines them, not other libs. */1208#ifdef HB_NO_VISIBILITY1209#undef HB_NO_VISIBILITY1210#include "hb-static.cc"1211#define HB_NO_VISIBILITY 11212#endif121312141215