Path: blob/master/thirdparty/harfbuzz/src/hb-common.cc
9913 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"303132/**33* SECTION:hb-common34* @title: hb-common35* @short_description: Common data types36* @include: hb.h37*38* Common data types used across HarfBuzz are defined here.39**/404142/* hb_options_t */4344hb_atomic_t<unsigned> _hb_options;4546void47_hb_options_init ()48{49hb_options_union_t u;50u.i = 0;51u.opts.initialized = true;5253const char *c = getenv ("HB_OPTIONS");54if (c)55{56while (*c)57{58const char *p = strchr (c, ':');59if (!p)60p = c + strlen (c);6162#define OPTION(name, symbol) \63if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0)6465OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);6667#undef OPTION6869c = *p ? p + 1 : p;70}7172}7374/* This is idempotent and threadsafe. */75_hb_options = u.i;76}777879/* hb_tag_t */8081/**82* hb_tag_from_string:83* @str: (array length=len) (element-type uint8_t): String to convert84* @len: Length of @str, or -1 if it is `NULL`-terminated85*86* Converts a string into an #hb_tag_t. Valid tags87* are four characters. Shorter input strings will be88* padded with spaces. Longer input strings will be89* truncated.90*91* Return value: The #hb_tag_t corresponding to @str92*93* Since: 0.9.294**/95hb_tag_t96hb_tag_from_string (const char *str, int len)97{98char tag[4];99unsigned int i;100101if (!str || !len || !*str)102return HB_TAG_NONE;103104if (len < 0 || len > 4)105len = 4;106for (i = 0; i < (unsigned) len && str[i]; i++)107tag[i] = str[i];108for (; i < 4; i++)109tag[i] = ' ';110111return HB_TAG (tag[0], tag[1], tag[2], tag[3]);112}113114/**115* hb_tag_to_string:116* @tag: #hb_tag_t to convert117* @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string118*119* Converts an #hb_tag_t to a string and returns it in @buf.120* Strings will be four characters long.121*122* Since: 0.9.5123**/124void125hb_tag_to_string (hb_tag_t tag, char *buf)126{127buf[0] = (char) (uint8_t) (tag >> 24);128buf[1] = (char) (uint8_t) (tag >> 16);129buf[2] = (char) (uint8_t) (tag >> 8);130buf[3] = (char) (uint8_t) (tag >> 0);131}132133134/* hb_direction_t */135136static const char direction_strings[][4] = {137"ltr",138"rtl",139"ttb",140"btt"141};142143/**144* hb_direction_from_string:145* @str: (array length=len) (element-type uint8_t): String to convert146* @len: Length of @str, or -1 if it is `NULL`-terminated147*148* Converts a string to an #hb_direction_t.149*150* Matching is loose and applies only to the first letter. For151* examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.152*153* Unmatched strings will return #HB_DIRECTION_INVALID.154*155* Return value: The #hb_direction_t matching @str156*157* Since: 0.9.2158**/159hb_direction_t160hb_direction_from_string (const char *str, int len)161{162if (unlikely (!str || !len || !*str))163return HB_DIRECTION_INVALID;164165/* Lets match loosely: just match the first letter, such that166* all of "ltr", "left-to-right", etc work!167*/168char c = TOLOWER (str[0]);169for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)170if (c == direction_strings[i][0])171return (hb_direction_t) (HB_DIRECTION_LTR + i);172173return HB_DIRECTION_INVALID;174}175176/**177* hb_direction_to_string:178* @direction: The #hb_direction_t to convert179*180* Converts an #hb_direction_t to a string.181*182* Return value: (transfer none): The string corresponding to @direction183*184* Since: 0.9.2185**/186const char *187hb_direction_to_string (hb_direction_t direction)188{189if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)190< ARRAY_LENGTH (direction_strings)))191return direction_strings[direction - HB_DIRECTION_LTR];192193return "invalid";194}195196197/* hb_language_t */198199struct hb_language_impl_t {200const char s[1];201};202203static const char canon_map[256] = {2040, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,2050, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,2060, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,207'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,2080, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',209'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',2100, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',211'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0212};213214static bool215lang_equal (hb_language_t v1,216const void *v2)217{218const unsigned char *p1 = (const unsigned char *) v1;219const unsigned char *p2 = (const unsigned char *) v2;220221while (*p1 && *p1 == canon_map[*p2]) {222p1++;223p2++;224}225226return *p1 == canon_map[*p2];227}228229#if 0230static unsigned int231lang_hash (const void *key)232{233const unsigned char *p = key;234unsigned int h = 0;235while (canon_map[*p])236{237h = (h << 5) - h + canon_map[*p];238p++;239}240241return h;242}243#endif244245246struct hb_language_item_t {247248struct hb_language_item_t *next;249hb_language_t lang;250251bool operator == (const char *s) const252{ return lang_equal (lang, s); }253254hb_language_item_t & operator = (const char *s)255{256/* We can't call strdup(), because we allow custom allocators. */257size_t len = strlen(s) + 1;258lang = (hb_language_t) hb_malloc(len);259if (likely (lang))260{261hb_memcpy((unsigned char *) lang, s, len);262for (unsigned char *p = (unsigned char *) lang; *p; p++)263*p = canon_map[*p];264}265266return *this;267}268269void fini () { hb_free ((void *) lang); }270};271272273/* Thread-safe lockfree language list */274275static hb_atomic_t<hb_language_item_t *> langs;276277static inline void278free_langs ()279{280retry:281hb_language_item_t *first_lang = langs;282if (unlikely (!langs.cmpexch (first_lang, nullptr)))283goto retry;284285while (first_lang) {286hb_language_item_t *next = first_lang->next;287first_lang->fini ();288hb_free (first_lang);289first_lang = next;290}291}292293static hb_language_item_t *294lang_find_or_insert (const char *key)295{296retry:297hb_language_item_t *first_lang = langs;298299for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)300if (*lang == key)301return lang;302303/* Not found; allocate one. */304hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));305if (unlikely (!lang))306return nullptr;307lang->next = first_lang;308*lang = key;309if (unlikely (!lang->lang))310{311hb_free (lang);312return nullptr;313}314315if (unlikely (!langs.cmpexch (first_lang, lang)))316{317lang->fini ();318hb_free (lang);319goto retry;320}321322if (!first_lang)323hb_atexit (free_langs); /* First person registers atexit() callback. */324325return lang;326}327328329/**330* hb_language_from_string:331* @str: (array length=len) (element-type uint8_t): a string representing332* a BCP 47 language tag333* @len: length of the @str, or -1 if it is `NULL`-terminated.334*335* Converts @str representing a BCP 47 language tag to the corresponding336* #hb_language_t.337*338* Return value: (transfer none):339* The #hb_language_t corresponding to the BCP 47 language tag.340*341* Since: 0.9.2342**/343hb_language_t344hb_language_from_string (const char *str, int len)345{346if (!str || !len || !*str)347return HB_LANGUAGE_INVALID;348349hb_language_item_t *item = nullptr;350if (len >= 0)351{352/* NUL-terminate it. */353char strbuf[64];354len = hb_min (len, (int) sizeof (strbuf) - 1);355hb_memcpy (strbuf, str, len);356strbuf[len] = '\0';357item = lang_find_or_insert (strbuf);358}359else360item = lang_find_or_insert (str);361362return likely (item) ? item->lang : HB_LANGUAGE_INVALID;363}364365/**366* hb_language_to_string:367* @language: The #hb_language_t to convert368*369* Converts an #hb_language_t to a string.370*371* Return value: (transfer none):372* A `NULL`-terminated string representing the @language. Must not be freed by373* the caller.374*375* Since: 0.9.2376**/377const char *378hb_language_to_string (hb_language_t language)379{380if (unlikely (!language)) return nullptr;381382return language->s;383}384385/**386* hb_language_get_default:387*388* Fetch the default language from current locale.389*390* <note>Note that the first time this function is called, it calls391* "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying392* setlocale function is, in many implementations, NOT threadsafe. To avoid393* problems, call this function once before multiple threads can call it.394* This function is only used from hb_buffer_guess_segment_properties() by395* HarfBuzz itself.</note>396*397* Return value: (transfer none): The default language of the locale as398* an #hb_language_t399*400* Since: 0.9.2401**/402hb_language_t403hb_language_get_default ()404{405static hb_atomic_t<hb_language_t> default_language;406407hb_language_t language = default_language;408if (unlikely (language == HB_LANGUAGE_INVALID))409{410language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1);411(void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);412}413414return language;415}416417/**418* hb_language_matches:419* @language: The #hb_language_t to work on420* @specific: Another #hb_language_t421*422* Check whether a second language tag is the same or a more423* specific version of the provided language tag. For example,424* "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR".425*426* Return value: `true` if languages match, `false` otherwise.427*428* Since: 5.0.0429**/430hb_bool_t431hb_language_matches (hb_language_t language,432hb_language_t specific)433{434if (language == specific) return true;435if (!language || !specific) return false;436437const char *l = language->s;438const char *s = specific->s;439unsigned ll = strlen (l);440unsigned sl = strlen (s);441442if (ll > sl)443return false;444445return strncmp (l, s, ll) == 0 &&446(s[ll] == '\0' || s[ll] == '-');447}448449450/* hb_script_t */451452/**453* hb_script_from_iso15924_tag:454* @tag: an #hb_tag_t representing an ISO 15924 tag.455*456* Converts an ISO 15924 script tag to a corresponding #hb_script_t.457*458* Return value:459* An #hb_script_t corresponding to the ISO 15924 tag.460*461* Since: 0.9.2462**/463hb_script_t464hb_script_from_iso15924_tag (hb_tag_t tag)465{466if (unlikely (tag == HB_TAG_NONE))467return HB_SCRIPT_INVALID;468469/* Be lenient, adjust case (one capital letter followed by three small letters) */470tag = (tag & 0xDFDFDFDFu) | 0x00202020u;471472switch (tag) {473474/* These graduated from the 'Q' private-area codes, but475* the old code is still aliased by Unicode, and the Qaai476* one in use by ICU. */477case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;478case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;479480/* Script variants from https://unicode.org/iso15924/ */481case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;482case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;483case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;484case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;485case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;486case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;487case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;488case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;489case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;490case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;491case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;492}493494/* If it looks right, just use the tag as a script */495if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)496return (hb_script_t) tag;497498/* Otherwise, return unknown */499return HB_SCRIPT_UNKNOWN;500}501502/**503* hb_script_from_string:504* @str: (array length=len) (element-type uint8_t): a string representing an505* ISO 15924 tag.506* @len: length of the @str, or -1 if it is `NULL`-terminated.507*508* Converts a string @str representing an ISO 15924 script tag to a509* corresponding #hb_script_t. Shorthand for hb_tag_from_string() then510* hb_script_from_iso15924_tag().511*512* Return value:513* An #hb_script_t corresponding to the ISO 15924 tag.514*515* Since: 0.9.2516**/517hb_script_t518hb_script_from_string (const char *str, int len)519{520return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));521}522523/**524* hb_script_to_iso15924_tag:525* @script: an #hb_script_t to convert.526*527* Converts an #hb_script_t to a corresponding ISO 15924 script tag.528*529* Return value:530* An #hb_tag_t representing an ISO 15924 script tag.531*532* Since: 0.9.2533**/534hb_tag_t535hb_script_to_iso15924_tag (hb_script_t script)536{537return (hb_tag_t) script;538}539540/**541* hb_script_get_horizontal_direction:542* @script: The #hb_script_t to query543*544* Fetches the #hb_direction_t of a script when it is545* set horizontally. All right-to-left scripts will return546* #HB_DIRECTION_RTL. All left-to-right scripts will return547* #HB_DIRECTION_LTR.548*549* Scripts that can be written either right-to-left or550* left-to-right will return #HB_DIRECTION_INVALID.551*552* Unknown scripts will return #HB_DIRECTION_LTR.553*554* Return value: The horizontal #hb_direction_t of @script555*556* Since: 0.9.2557**/558hb_direction_t559hb_script_get_horizontal_direction (hb_script_t script)560{561/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */562switch ((hb_tag_t) script)563{564/* Unicode-1.1 additions */565case HB_SCRIPT_ARABIC:566case HB_SCRIPT_HEBREW:567568/* Unicode-3.0 additions */569case HB_SCRIPT_SYRIAC:570case HB_SCRIPT_THAANA:571572/* Unicode-4.0 additions */573case HB_SCRIPT_CYPRIOT:574575/* Unicode-4.1 additions */576case HB_SCRIPT_KHAROSHTHI:577578/* Unicode-5.0 additions */579case HB_SCRIPT_PHOENICIAN:580case HB_SCRIPT_NKO:581582/* Unicode-5.1 additions */583case HB_SCRIPT_LYDIAN:584585/* Unicode-5.2 additions */586case HB_SCRIPT_AVESTAN:587case HB_SCRIPT_IMPERIAL_ARAMAIC:588case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:589case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:590case HB_SCRIPT_OLD_SOUTH_ARABIAN:591case HB_SCRIPT_OLD_TURKIC:592case HB_SCRIPT_SAMARITAN:593594/* Unicode-6.0 additions */595case HB_SCRIPT_MANDAIC:596597/* Unicode-6.1 additions */598case HB_SCRIPT_MEROITIC_CURSIVE:599case HB_SCRIPT_MEROITIC_HIEROGLYPHS:600601/* Unicode-7.0 additions */602case HB_SCRIPT_MANICHAEAN:603case HB_SCRIPT_MENDE_KIKAKUI:604case HB_SCRIPT_NABATAEAN:605case HB_SCRIPT_OLD_NORTH_ARABIAN:606case HB_SCRIPT_PALMYRENE:607case HB_SCRIPT_PSALTER_PAHLAVI:608609/* Unicode-8.0 additions */610case HB_SCRIPT_HATRAN:611612/* Unicode-9.0 additions */613case HB_SCRIPT_ADLAM:614615/* Unicode-11.0 additions */616case HB_SCRIPT_HANIFI_ROHINGYA:617case HB_SCRIPT_OLD_SOGDIAN:618case HB_SCRIPT_SOGDIAN:619620/* Unicode-12.0 additions */621case HB_SCRIPT_ELYMAIC:622623/* Unicode-13.0 additions */624case HB_SCRIPT_CHORASMIAN:625case HB_SCRIPT_YEZIDI:626627/* Unicode-14.0 additions */628case HB_SCRIPT_OLD_UYGHUR:629630/* Unicode-16.0 additions */631case HB_SCRIPT_GARAY:632633return HB_DIRECTION_RTL;634635636/* https://github.com/harfbuzz/harfbuzz/issues/1000 */637case HB_SCRIPT_OLD_HUNGARIAN:638case HB_SCRIPT_OLD_ITALIC:639case HB_SCRIPT_RUNIC:640case HB_SCRIPT_TIFINAGH:641642return HB_DIRECTION_INVALID;643}644645return HB_DIRECTION_LTR;646}647648649/* hb_version */650651652/**653* SECTION:hb-version654* @title: hb-version655* @short_description: Information about the version of HarfBuzz in use656* @include: hb.h657*658* These functions and macros allow accessing version of the HarfBuzz659* library used at compile- as well as run-time, and to direct code660* conditionally based on those versions, again, at compile- or run-time.661**/662663664/**665* hb_version:666* @major: (out): Library major version component667* @minor: (out): Library minor version component668* @micro: (out): Library micro version component669*670* Returns library version as three integer components.671*672* Since: 0.9.2673**/674void675hb_version (unsigned int *major,676unsigned int *minor,677unsigned int *micro)678{679*major = HB_VERSION_MAJOR;680*minor = HB_VERSION_MINOR;681*micro = HB_VERSION_MICRO;682}683684/**685* hb_version_string:686*687* Returns library version as a string with three components.688*689* Return value: Library version string690*691* Since: 0.9.2692**/693const char *694hb_version_string ()695{696return HB_VERSION_STRING;697}698699/**700* hb_version_atleast:701* @major: Library major version component702* @minor: Library minor version component703* @micro: Library micro version component704*705* Tests the library version against a minimum value,706* as three integer components.707*708* Return value: `true` if the library is equal to or greater than709* the test value, `false` otherwise710*711* Since: 0.9.30712**/713hb_bool_t714hb_version_atleast (unsigned int major,715unsigned int minor,716unsigned int micro)717{718return HB_VERSION_ATLEAST (major, minor, micro);719}720721722723/* hb_feature_t and hb_variation_t */724725static bool726parse_space (const char **pp, const char *end)727{728while (*pp < end && ISSPACE (**pp))729(*pp)++;730return true;731}732733static bool734parse_char (const char **pp, const char *end, char c)735{736parse_space (pp, end);737738if (*pp == end || **pp != c)739return false;740741(*pp)++;742return true;743}744745static bool746parse_uint (const char **pp, const char *end, unsigned int *pv)747{748/* Intentionally use hb_parse_int inside instead of hb_parse_uint,749* such that -1 turns into "big number"... */750int v;751if (unlikely (!hb_parse_int (pp, end, &v))) return false;752753*pv = v;754return true;755}756757static bool758parse_uint32 (const char **pp, const char *end, uint32_t *pv)759{760/* Intentionally use hb_parse_int inside instead of hb_parse_uint,761* such that -1 turns into "big number"... */762int v;763if (unlikely (!hb_parse_int (pp, end, &v))) return false;764765*pv = v;766return true;767}768769static bool770parse_bool (const char **pp, const char *end, uint32_t *pv)771{772parse_space (pp, end);773774const char *p = *pp;775while (*pp < end && ISALPHA(**pp))776(*pp)++;777778/* CSS allows on/off as aliases 1/0. */779if (*pp - p == 2780&& TOLOWER (p[0]) == 'o'781&& TOLOWER (p[1]) == 'n')782*pv = 1;783else if (*pp - p == 3784&& TOLOWER (p[0]) == 'o'785&& TOLOWER (p[1]) == 'f'786&& TOLOWER (p[2]) == 'f')787*pv = 0;788else789return false;790791return true;792}793794/* hb_feature_t */795796static bool797parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)798{799if (parse_char (pp, end, '-'))800feature->value = 0;801else {802parse_char (pp, end, '+');803feature->value = 1;804}805806return true;807}808809static bool810parse_tag (const char **pp, const char *end, hb_tag_t *tag)811{812parse_space (pp, end);813814char quote = 0;815816if (*pp < end && (**pp == '\'' || **pp == '"'))817{818quote = **pp;819(*pp)++;820}821822const char *p = *pp;823while (*pp < end && (**pp != ' ' && **pp != '=' && **pp != '[' && **pp != quote))824(*pp)++;825826if (p == *pp || *pp - p > 4)827return false;828829*tag = hb_tag_from_string (p, *pp - p);830831if (quote)832{833/* CSS expects exactly four bytes. And we only allow quotations for834* CSS compatibility. So, enforce the length. */835if (*pp - p != 4)836return false;837if (*pp == end || **pp != quote)838return false;839(*pp)++;840}841842return true;843}844845static bool846parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)847{848parse_space (pp, end);849850bool has_start;851852feature->start = HB_FEATURE_GLOBAL_START;853feature->end = HB_FEATURE_GLOBAL_END;854855if (!parse_char (pp, end, '['))856return true;857858has_start = parse_uint (pp, end, &feature->start);859860if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {861parse_uint (pp, end, &feature->end);862} else {863if (has_start)864feature->end = feature->start + 1;865}866867return parse_char (pp, end, ']');868}869870static bool871parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)872{873bool had_equal = parse_char (pp, end, '=');874bool had_value = parse_uint32 (pp, end, &feature->value) ||875parse_bool (pp, end, &feature->value);876/* CSS doesn't use equal-sign between tag and value.877* If there was an equal-sign, then there *must* be a value.878* A value without an equal-sign is ok, but not required. */879return !had_equal || had_value;880}881882static bool883parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)884{885return parse_feature_value_prefix (pp, end, feature) &&886parse_tag (pp, end, &feature->tag) &&887parse_feature_indices (pp, end, feature) &&888parse_feature_value_postfix (pp, end, feature) &&889parse_space (pp, end) &&890*pp == end;891}892893/**894* hb_feature_from_string:895* @str: (array length=len) (element-type uint8_t): a string to parse896* @len: length of @str, or -1 if string is `NULL` terminated897* @feature: (out): the #hb_feature_t to initialize with the parsed values898*899* Parses a string into a #hb_feature_t.900*901* The format for specifying feature strings follows. All valid CSS902* font-feature-settings values other than 'normal' and the global values are903* also accepted, though not documented below. CSS string escapes are not904* supported.905*906* The range indices refer to the positions between Unicode characters. The907* position before the first character is always 0.908*909* The format is Python-esque. Here is how it all works:910*911* <informaltable pgwide='1' align='left' frame='none'>912* <tgroup cols='5'>913* <thead>914* <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>915* </thead>916* <tbody>917* <row><entry>Setting value:</entry></row>918* <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>919* <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>920* <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>921* <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>922* <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>923* <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>924* <row><entry>Setting index:</entry></row>925* <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>926* <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>927* <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>928* <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>929* <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>930* <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>931* <row><entry>Mixing it all:</entry></row>932* <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>933* </tbody>934* </tgroup>935* </informaltable>936*937* Return value:938* `true` if @str is successfully parsed, `false` otherwise939*940* Since: 0.9.5941**/942hb_bool_t943hb_feature_from_string (const char *str, int len,944hb_feature_t *feature)945{946hb_feature_t feat;947948if (len < 0)949len = strlen (str);950951if (likely (parse_one_feature (&str, str + len, &feat)))952{953if (feature)954*feature = feat;955return true;956}957958if (feature)959hb_memset (feature, 0, sizeof (*feature));960return false;961}962963/**964* hb_feature_to_string:965* @feature: an #hb_feature_t to convert966* @buf: (array length=size) (out): output string967* @size: the allocated size of @buf968*969* Converts a #hb_feature_t into a `NULL`-terminated string in the format970* understood by hb_feature_from_string(). The client in responsible for971* allocating big enough size for @buf, 128 bytes is more than enough.972*973* Note that the feature value will be omitted if it is '1', but the974* string won't include any whitespace.975*976* Since: 0.9.5977**/978void979hb_feature_to_string (hb_feature_t *feature,980char *buf, unsigned int size)981{982if (unlikely (!size)) return;983984char s[128];985unsigned int len = 0;986if (feature->value == 0)987s[len++] = '-';988hb_tag_to_string (feature->tag, s + len);989len += 4;990while (len && s[len - 1] == ' ')991len--;992if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)993{994s[len++] = '[';995if (feature->start)996len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));997if (feature->end != feature->start + 1) {998s[len++] = ':';999if (feature->end != HB_FEATURE_GLOBAL_END)1000len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));1001}1002s[len++] = ']';1003}1004if (feature->value > 1)1005{1006s[len++] = '=';1007len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%" PRIu32, feature->value));1008}1009assert (len < ARRAY_LENGTH (s));1010len = hb_min (len, size - 1);1011hb_memcpy (buf, s, len);1012buf[len] = '\0';1013}10141015/* hb_variation_t */10161017static bool1018parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)1019{1020parse_char (pp, end, '='); /* Optional. */1021double v;1022if (unlikely (!hb_parse_double (pp, end, &v))) return false;10231024variation->value = v;1025return true;1026}10271028static bool1029parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)1030{1031return parse_tag (pp, end, &variation->tag) &&1032parse_variation_value (pp, end, variation) &&1033parse_space (pp, end) &&1034*pp == end;1035}10361037/**1038* hb_variation_from_string:1039* @str: (array length=len) (element-type uint8_t): a string to parse1040* @len: length of @str, or -1 if string is `NULL` terminated1041* @variation: (out): the #hb_variation_t to initialize with the parsed values1042*1043* Parses a string into a #hb_variation_t.1044*1045* The format for specifying variation settings follows. All valid CSS1046* font-variation-settings values other than 'normal' and 'inherited' are also1047* accepted, though, not documented below.1048*1049* The format is a tag, optionally followed by an equals sign, followed by a1050* number. For example `wght=500`, or `slnt=-7.5`.1051*1052* Return value:1053* `true` if @str is successfully parsed, `false` otherwise1054*1055* Since: 1.4.21056*/1057hb_bool_t1058hb_variation_from_string (const char *str, int len,1059hb_variation_t *variation)1060{1061hb_variation_t var;10621063if (len < 0)1064len = strlen (str);10651066if (likely (parse_one_variation (&str, str + len, &var)))1067{1068if (variation)1069*variation = var;1070return true;1071}10721073if (variation)1074hb_memset (variation, 0, sizeof (*variation));1075return false;1076}10771078#ifndef HB_NO_SETLOCALE10791080static inline void free_static_C_locale ();10811082static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,1083hb_C_locale_lazy_loader_t>1084{1085static hb_locale_t create ()1086{1087hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);1088if (!l)1089return l;10901091hb_atexit (free_static_C_locale);10921093return l;1094}1095static void destroy (hb_locale_t l)1096{1097freelocale (l);1098}1099static hb_locale_t get_null ()1100{1101return (hb_locale_t) 0;1102}1103} static_C_locale;11041105static inline1106void free_static_C_locale ()1107{1108static_C_locale.free_instance ();1109}11101111static hb_locale_t1112get_C_locale ()1113{1114return static_C_locale.get_unconst ();1115}11161117#endif11181119/**1120* hb_variation_to_string:1121* @variation: an #hb_variation_t to convert1122* @buf: (array length=size) (out caller-allocates): output string1123* @size: the allocated size of @buf1124*1125* Converts an #hb_variation_t into a `NULL`-terminated string in the format1126* understood by hb_variation_from_string(). The client in responsible for1127* allocating big enough size for @buf, 128 bytes is more than enough.1128*1129* Note that the string won't include any whitespace.1130*1131* Since: 1.4.21132*/1133void1134hb_variation_to_string (hb_variation_t *variation,1135char *buf, unsigned int size)1136{1137if (unlikely (!size)) return;11381139char s[128];1140unsigned int len = 0;1141hb_tag_to_string (variation->tag, s + len);1142len += 4;1143while (len && s[len - 1] == ' ')1144len--;1145s[len++] = '=';11461147hb_locale_t oldlocale HB_UNUSED;1148oldlocale = hb_uselocale (get_C_locale ());1149len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));1150(void) hb_uselocale (oldlocale);11511152assert (len < ARRAY_LENGTH (s));1153len = hb_min (len, size - 1);1154hb_memcpy (buf, s, len);1155buf[len] = '\0';1156}11571158/**1159* hb_color_get_alpha:1160* @color: an #hb_color_t we are interested in its channels.1161*1162* Fetches the alpha channel of the given @color.1163*1164* Return value: Alpha channel value1165*1166* Since: 2.1.01167*/1168uint8_t1169(hb_color_get_alpha) (hb_color_t color)1170{1171return hb_color_get_alpha (color);1172}11731174/**1175* hb_color_get_red:1176* @color: an #hb_color_t we are interested in its channels.1177*1178* Fetches the red channel of the given @color.1179*1180* Return value: Red channel value1181*1182* Since: 2.1.01183*/1184uint8_t1185(hb_color_get_red) (hb_color_t color)1186{1187return hb_color_get_red (color);1188}11891190/**1191* hb_color_get_green:1192* @color: an #hb_color_t we are interested in its channels.1193*1194* Fetches the green channel of the given @color.1195*1196* Return value: Green channel value1197*1198* Since: 2.1.01199*/1200uint8_t1201(hb_color_get_green) (hb_color_t color)1202{1203return hb_color_get_green (color);1204}12051206/**1207* hb_color_get_blue:1208* @color: an #hb_color_t we are interested in its channels.1209*1210* Fetches the blue channel of the given @color.1211*1212* Return value: Blue channel value1213*1214* Since: 2.1.01215*/1216uint8_t1217(hb_color_get_blue) (hb_color_t color)1218{1219return hb_color_get_blue (color);1220}12211222/**1223* hb_malloc:1224* @size: The size of the memory to allocate.1225*1226* Allocates @size bytes of memory, using the allocator set at1227* compile-time. Typically just malloc().1228*1229* Return value: A pointer to the allocated memory.1230*1231* Since: 11.0.01232**/1233void* hb_malloc(size_t size) { return hb_malloc_impl (size); }12341235/**1236* hb_calloc:1237* @nmemb: The number of elements to allocate.1238* @size: The size of each element.1239*1240* Allocates @nmemb elements of @size bytes each, initialized to zero,1241* using the allocator set at compile-time. Typically just calloc().1242*1243* Return value: A pointer to the allocated memory.1244*1245* Since: 11.0.01246**/1247void* hb_calloc(size_t nmemb, size_t size) { return hb_calloc_impl (nmemb, size); }12481249/**1250* hb_realloc:1251* @ptr: The pointer to the memory to reallocate.1252* @size: The new size of the memory.1253*1254* Reallocates the memory pointed to by @ptr to @size bytes, using the1255* allocator set at compile-time. Typically just realloc().1256*1257* Return value: A pointer to the reallocated memory.1258*1259* Since: 11.0.01260**/1261void* hb_realloc(void *ptr, size_t size) { return hb_realloc_impl (ptr, size); }12621263/**1264* hb_free:1265* @ptr: The pointer to the memory to free.1266*1267* Frees the memory pointed to by @ptr, using the allocator set at1268* compile-time. Typically just free().1269*1270* Since: 11.0.01271**/1272void hb_free(void *ptr) { hb_free_impl (ptr); }127312741275/* If there is no visibility control, then hb-static.cc will NOT1276* define anything. Instead, we get it to define one set in here1277* only, so only libharfbuzz.so defines them, not other libs. */1278#ifdef HB_NO_VISIBILITY1279#undef HB_NO_VISIBILITY1280#include "hb-static.cc"1281#define HB_NO_VISIBILITY 11282#endif128312841285