Path: blob/master/thirdparty/harfbuzz/src/hb-common.cc
21318 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_tag_t */4344/**45* hb_tag_from_string:46* @str: (array length=len) (element-type uint8_t): String to convert47* @len: Length of @str, or -1 if it is `NULL`-terminated48*49* Converts a string into an #hb_tag_t. Valid tags50* are four characters. Shorter input strings will be51* padded with spaces. Longer input strings will be52* truncated.53*54* Return value: The #hb_tag_t corresponding to @str55*56* Since: 0.9.257**/58hb_tag_t59hb_tag_from_string (const char *str, int len)60{61char tag[4];62unsigned int i;6364if (!str || !len || !*str)65return HB_TAG_NONE;6667if (len < 0 || len > 4)68len = 4;69for (i = 0; i < (unsigned) len && str[i]; i++)70tag[i] = str[i];71for (; i < 4; i++)72tag[i] = ' ';7374return HB_TAG (tag[0], tag[1], tag[2], tag[3]);75}7677/**78* hb_tag_to_string:79* @tag: #hb_tag_t to convert80* @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string81*82* Converts an #hb_tag_t to a string and returns it in @buf.83* Strings will be four characters long.84*85* Since: 0.9.586**/87void88hb_tag_to_string (hb_tag_t tag, char *buf)89{90buf[0] = (char) (uint8_t) (tag >> 24);91buf[1] = (char) (uint8_t) (tag >> 16);92buf[2] = (char) (uint8_t) (tag >> 8);93buf[3] = (char) (uint8_t) (tag >> 0);94}959697/* hb_direction_t */9899static const char direction_strings[][4] = {100"ltr",101"rtl",102"ttb",103"btt"104};105106/**107* hb_direction_from_string:108* @str: (array length=len) (element-type uint8_t): String to convert109* @len: Length of @str, or -1 if it is `NULL`-terminated110*111* Converts a string to an #hb_direction_t.112*113* Matching is loose and applies only to the first letter. For114* examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.115*116* Unmatched strings will return #HB_DIRECTION_INVALID.117*118* Return value: The #hb_direction_t matching @str119*120* Since: 0.9.2121**/122hb_direction_t123hb_direction_from_string (const char *str, int len)124{125if (unlikely (!str || !len || !*str))126return HB_DIRECTION_INVALID;127128/* Lets match loosely: just match the first letter, such that129* all of "ltr", "left-to-right", etc work!130*/131char c = TOLOWER (str[0]);132for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)133if (c == direction_strings[i][0])134return (hb_direction_t) (HB_DIRECTION_LTR + i);135136return HB_DIRECTION_INVALID;137}138139/**140* hb_direction_to_string:141* @direction: The #hb_direction_t to convert142*143* Converts an #hb_direction_t to a string.144*145* Return value: (transfer none): The string corresponding to @direction146*147* Since: 0.9.2148**/149const char *150hb_direction_to_string (hb_direction_t direction)151{152if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)153< ARRAY_LENGTH (direction_strings)))154return direction_strings[direction - HB_DIRECTION_LTR];155156return "invalid";157}158159160/* hb_language_t */161162struct hb_language_impl_t {163const char s[1];164};165166static const char canon_map[256] = {1670, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1680, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1690, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,170'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,1710, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',172'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',1730, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',174'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0175};176177static bool178lang_equal (hb_language_t v1,179const void *v2)180{181const unsigned char *p1 = (const unsigned char *) v1;182const unsigned char *p2 = (const unsigned char *) v2;183184while (*p1 && *p1 == canon_map[*p2]) {185p1++;186p2++;187}188189return *p1 == canon_map[*p2];190}191192#if 0193static unsigned int194lang_hash (const void *key)195{196const unsigned char *p = key;197unsigned int h = 0;198while (canon_map[*p])199{200h = (h << 5) - h + canon_map[*p];201p++;202}203204return h;205}206#endif207208209struct hb_language_item_t {210211struct hb_language_item_t *next;212hb_language_t lang;213214bool operator == (const char *s) const215{ return lang_equal (lang, s); }216217hb_language_item_t & operator = (const char *s)218{219/* We can't call strdup(), because we allow custom allocators. */220size_t len = strlen(s) + 1;221lang = (hb_language_t) hb_malloc(len);222if (likely (lang))223{224hb_memcpy((unsigned char *) lang, s, len);225for (unsigned char *p = (unsigned char *) lang; *p; p++)226*p = canon_map[*p];227}228229return *this;230}231232void fini () { hb_free ((void *) lang); }233};234235236/* Thread-safe lockfree language list */237238static hb_atomic_t<hb_language_item_t *> langs;239240static inline void241free_langs ()242{243retry:244hb_language_item_t *first_lang = langs;245if (unlikely (!langs.cmpexch (first_lang, nullptr)))246goto retry;247248while (first_lang) {249hb_language_item_t *next = first_lang->next;250first_lang->fini ();251hb_free (first_lang);252first_lang = next;253}254}255256static hb_language_item_t *257lang_find_or_insert (const char *key)258{259retry:260hb_language_item_t *first_lang = langs;261262for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)263if (*lang == key)264return lang;265266/* Not found; allocate one. */267hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));268if (unlikely (!lang))269return nullptr;270lang->next = first_lang;271*lang = key;272if (unlikely (!lang->lang))273{274hb_free (lang);275return nullptr;276}277278if (unlikely (!langs.cmpexch (first_lang, lang)))279{280lang->fini ();281hb_free (lang);282goto retry;283}284285if (!first_lang)286hb_atexit (free_langs); /* First person registers atexit() callback. */287288return lang;289}290291292/**293* hb_language_from_string:294* @str: (array length=len) (element-type uint8_t): a string representing295* a BCP 47 language tag296* @len: length of the @str, or -1 if it is `NULL`-terminated.297*298* Converts @str representing a BCP 47 language tag to the corresponding299* #hb_language_t.300*301* Return value: (transfer none):302* The #hb_language_t corresponding to the BCP 47 language tag.303*304* Since: 0.9.2305**/306hb_language_t307hb_language_from_string (const char *str, int len)308{309if (!str || !len || !*str)310return HB_LANGUAGE_INVALID;311312hb_language_item_t *item = nullptr;313if (len >= 0)314{315/* NUL-terminate it. */316char strbuf[64];317len = hb_min (len, (int) sizeof (strbuf) - 1);318hb_memcpy (strbuf, str, len);319strbuf[len] = '\0';320item = lang_find_or_insert (strbuf);321}322else323item = lang_find_or_insert (str);324325return likely (item) ? item->lang : HB_LANGUAGE_INVALID;326}327328/**329* hb_language_to_string:330* @language: The #hb_language_t to convert331*332* Converts an #hb_language_t to a string.333*334* Return value: (transfer none):335* A `NULL`-terminated string representing the @language. Must not be freed by336* the caller.337*338* Since: 0.9.2339**/340const char *341hb_language_to_string (hb_language_t language)342{343if (unlikely (!language)) return nullptr;344345return language->s;346}347348/**349* hb_language_get_default:350*351* Fetch the default language from current locale.352*353* <note>Note that the first time this function is called, it calls354* "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying355* setlocale function is, in many implementations, NOT threadsafe. To avoid356* problems, call this function once before multiple threads can call it.357* This function is only used from hb_buffer_guess_segment_properties() by358* HarfBuzz itself.</note>359*360* Return value: (transfer none): The default language of the locale as361* an #hb_language_t362*363* Since: 0.9.2364**/365hb_language_t366hb_language_get_default ()367{368static hb_atomic_t<hb_language_t> default_language;369370hb_language_t language = default_language;371if (unlikely (language == HB_LANGUAGE_INVALID))372{373language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1);374(void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);375}376377return language;378}379380/**381* hb_language_matches:382* @language: The #hb_language_t to work on383* @specific: Another #hb_language_t384*385* Check whether a second language tag is the same or a more386* specific version of the provided language tag. For example,387* "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR".388*389* Return value: `true` if languages match, `false` otherwise.390*391* Since: 5.0.0392**/393hb_bool_t394hb_language_matches (hb_language_t language,395hb_language_t specific)396{397if (language == specific) return true;398if (!language || !specific) return false;399400const char *l = language->s;401const char *s = specific->s;402unsigned ll = strlen (l);403unsigned sl = strlen (s);404405if (ll > sl)406return false;407408return strncmp (l, s, ll) == 0 &&409(s[ll] == '\0' || s[ll] == '-');410}411412413/* hb_script_t */414415/**416* hb_script_from_iso15924_tag:417* @tag: an #hb_tag_t representing an ISO 15924 tag.418*419* Converts an ISO 15924 script tag to a corresponding #hb_script_t.420*421* Return value:422* An #hb_script_t corresponding to the ISO 15924 tag.423*424* Since: 0.9.2425**/426hb_script_t427hb_script_from_iso15924_tag (hb_tag_t tag)428{429if (unlikely (tag == HB_TAG_NONE))430return HB_SCRIPT_INVALID;431432/* Be lenient, adjust case (one capital letter followed by three small letters) */433tag = (tag & 0xDFDFDFDFu) | 0x00202020u;434435switch (tag) {436437/* These graduated from the 'Q' private-area codes, but438* the old code is still aliased by Unicode, and the Qaai439* one in use by ICU. */440case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;441case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;442443/* Script variants from https://unicode.org/iso15924/ */444case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;445case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;446case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;447case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;448case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;449case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;450case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;451case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;452case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;453case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;454case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;455}456457/* If it looks right, just use the tag as a script */458if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)459return (hb_script_t) tag;460461/* Otherwise, return unknown */462return HB_SCRIPT_UNKNOWN;463}464465/**466* hb_script_from_string:467* @str: (array length=len) (element-type uint8_t): a string representing an468* ISO 15924 tag.469* @len: length of the @str, or -1 if it is `NULL`-terminated.470*471* Converts a string @str representing an ISO 15924 script tag to a472* corresponding #hb_script_t. Shorthand for hb_tag_from_string() then473* hb_script_from_iso15924_tag().474*475* Return value:476* An #hb_script_t corresponding to the ISO 15924 tag.477*478* Since: 0.9.2479**/480hb_script_t481hb_script_from_string (const char *str, int len)482{483return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));484}485486/**487* hb_script_to_iso15924_tag:488* @script: an #hb_script_t to convert.489*490* Converts an #hb_script_t to a corresponding ISO 15924 script tag.491*492* Return value:493* An #hb_tag_t representing an ISO 15924 script tag.494*495* Since: 0.9.2496**/497hb_tag_t498hb_script_to_iso15924_tag (hb_script_t script)499{500return (hb_tag_t) script;501}502503/**504* hb_script_get_horizontal_direction:505* @script: The #hb_script_t to query506*507* Fetches the #hb_direction_t of a script when it is508* set horizontally. All right-to-left scripts will return509* #HB_DIRECTION_RTL. All left-to-right scripts will return510* #HB_DIRECTION_LTR.511*512* Scripts that can be written either right-to-left or513* left-to-right will return #HB_DIRECTION_INVALID.514*515* Unknown scripts will return #HB_DIRECTION_LTR.516*517* Return value: The horizontal #hb_direction_t of @script518*519* Since: 0.9.2520**/521hb_direction_t522hb_script_get_horizontal_direction (hb_script_t script)523{524/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */525switch ((hb_tag_t) script)526{527/* Unicode-1.1 additions */528case HB_SCRIPT_ARABIC:529case HB_SCRIPT_HEBREW:530531/* Unicode-3.0 additions */532case HB_SCRIPT_SYRIAC:533case HB_SCRIPT_THAANA:534535/* Unicode-4.0 additions */536case HB_SCRIPT_CYPRIOT:537538/* Unicode-4.1 additions */539case HB_SCRIPT_KHAROSHTHI:540541/* Unicode-5.0 additions */542case HB_SCRIPT_PHOENICIAN:543case HB_SCRIPT_NKO:544545/* Unicode-5.1 additions */546case HB_SCRIPT_LYDIAN:547548/* Unicode-5.2 additions */549case HB_SCRIPT_AVESTAN:550case HB_SCRIPT_IMPERIAL_ARAMAIC:551case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:552case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:553case HB_SCRIPT_OLD_SOUTH_ARABIAN:554case HB_SCRIPT_OLD_TURKIC:555case HB_SCRIPT_SAMARITAN:556557/* Unicode-6.0 additions */558case HB_SCRIPT_MANDAIC:559560/* Unicode-6.1 additions */561case HB_SCRIPT_MEROITIC_CURSIVE:562case HB_SCRIPT_MEROITIC_HIEROGLYPHS:563564/* Unicode-7.0 additions */565case HB_SCRIPT_MANICHAEAN:566case HB_SCRIPT_MENDE_KIKAKUI:567case HB_SCRIPT_NABATAEAN:568case HB_SCRIPT_OLD_NORTH_ARABIAN:569case HB_SCRIPT_PALMYRENE:570case HB_SCRIPT_PSALTER_PAHLAVI:571572/* Unicode-8.0 additions */573case HB_SCRIPT_HATRAN:574575/* Unicode-9.0 additions */576case HB_SCRIPT_ADLAM:577578/* Unicode-11.0 additions */579case HB_SCRIPT_HANIFI_ROHINGYA:580case HB_SCRIPT_OLD_SOGDIAN:581case HB_SCRIPT_SOGDIAN:582583/* Unicode-12.0 additions */584case HB_SCRIPT_ELYMAIC:585586/* Unicode-13.0 additions */587case HB_SCRIPT_CHORASMIAN:588case HB_SCRIPT_YEZIDI:589590/* Unicode-14.0 additions */591case HB_SCRIPT_OLD_UYGHUR:592593/* Unicode-16.0 additions */594case HB_SCRIPT_GARAY:595596/* Unicode-17.0 additions */597case HB_SCRIPT_SIDETIC:598599return HB_DIRECTION_RTL;600601602/* https://github.com/harfbuzz/harfbuzz/issues/1000 */603case HB_SCRIPT_OLD_HUNGARIAN:604case HB_SCRIPT_OLD_ITALIC:605case HB_SCRIPT_RUNIC:606case HB_SCRIPT_TIFINAGH:607608return HB_DIRECTION_INVALID;609}610611return HB_DIRECTION_LTR;612}613614615/* hb_version */616617618/**619* SECTION:hb-version620* @title: hb-version621* @short_description: Information about the version of HarfBuzz in use622* @include: hb.h623*624* These functions and macros allow accessing version of the HarfBuzz625* library used at compile- as well as run-time, and to direct code626* conditionally based on those versions, again, at compile- or run-time.627**/628629630/**631* hb_version:632* @major: (out): Library major version component633* @minor: (out): Library minor version component634* @micro: (out): Library micro version component635*636* Returns library version as three integer components.637*638* Since: 0.9.2639**/640void641hb_version (unsigned int *major,642unsigned int *minor,643unsigned int *micro)644{645*major = HB_VERSION_MAJOR;646*minor = HB_VERSION_MINOR;647*micro = HB_VERSION_MICRO;648}649650/**651* hb_version_string:652*653* Returns library version as a string with three components.654*655* Return value: Library version string656*657* Since: 0.9.2658**/659const char *660hb_version_string ()661{662return HB_VERSION_STRING;663}664665/**666* hb_version_atleast:667* @major: Library major version component668* @minor: Library minor version component669* @micro: Library micro version component670*671* Tests the library version against a minimum value,672* as three integer components.673*674* Return value: `true` if the library is equal to or greater than675* the test value, `false` otherwise676*677* Since: 0.9.30678**/679hb_bool_t680hb_version_atleast (unsigned int major,681unsigned int minor,682unsigned int micro)683{684return HB_VERSION_ATLEAST (major, minor, micro);685}686687688689/* hb_feature_t and hb_variation_t */690691static bool692parse_space (const char **pp, const char *end)693{694while (*pp < end && ISSPACE (**pp))695(*pp)++;696return true;697}698699static bool700parse_char (const char **pp, const char *end, char c)701{702parse_space (pp, end);703704if (*pp == end || **pp != c)705return false;706707(*pp)++;708return true;709}710711static bool712parse_uint (const char **pp, const char *end, unsigned int *pv)713{714/* Intentionally use hb_parse_int inside instead of hb_parse_uint,715* such that -1 turns into "big number"... */716int v;717if (unlikely (!hb_parse_int (pp, end, &v))) return false;718719*pv = v;720return true;721}722723static bool724parse_uint32 (const char **pp, const char *end, uint32_t *pv)725{726/* Intentionally use hb_parse_int inside instead of hb_parse_uint,727* such that -1 turns into "big number"... */728int v;729if (unlikely (!hb_parse_int (pp, end, &v))) return false;730731*pv = v;732return true;733}734735static bool736parse_bool (const char **pp, const char *end, uint32_t *pv)737{738parse_space (pp, end);739740const char *p = *pp;741while (*pp < end && ISALPHA(**pp))742(*pp)++;743744/* CSS allows on/off as aliases 1/0. */745if (*pp - p == 2746&& TOLOWER (p[0]) == 'o'747&& TOLOWER (p[1]) == 'n')748*pv = 1;749else if (*pp - p == 3750&& TOLOWER (p[0]) == 'o'751&& TOLOWER (p[1]) == 'f'752&& TOLOWER (p[2]) == 'f')753*pv = 0;754else755return false;756757return true;758}759760/* hb_feature_t */761762static bool763parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)764{765if (parse_char (pp, end, '-'))766feature->value = 0;767else {768parse_char (pp, end, '+');769feature->value = 1;770}771772return true;773}774775static bool776parse_tag (const char **pp, const char *end, hb_tag_t *tag)777{778parse_space (pp, end);779780char quote = 0;781782if (*pp < end && (**pp == '\'' || **pp == '"'))783{784quote = **pp;785(*pp)++;786}787788const char *p = *pp;789while (*pp < end && (**pp != ' ' && **pp != '=' && **pp != '[' && **pp != quote))790(*pp)++;791792if (p == *pp || *pp - p > 4)793return false;794795*tag = hb_tag_from_string (p, *pp - p);796797if (quote)798{799/* CSS expects exactly four bytes. And we only allow quotations for800* CSS compatibility. So, enforce the length. */801if (*pp - p != 4)802return false;803if (*pp == end || **pp != quote)804return false;805(*pp)++;806}807808return true;809}810811static bool812parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)813{814parse_space (pp, end);815816bool has_start;817818feature->start = HB_FEATURE_GLOBAL_START;819feature->end = HB_FEATURE_GLOBAL_END;820821if (!parse_char (pp, end, '['))822return true;823824has_start = parse_uint (pp, end, &feature->start);825826if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {827parse_uint (pp, end, &feature->end);828} else {829if (has_start)830feature->end = feature->start + 1;831}832833return parse_char (pp, end, ']');834}835836static bool837parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)838{839bool had_equal = parse_char (pp, end, '=');840bool had_value = parse_uint32 (pp, end, &feature->value) ||841parse_bool (pp, end, &feature->value);842/* CSS doesn't use equal-sign between tag and value.843* If there was an equal-sign, then there *must* be a value.844* A value without an equal-sign is ok, but not required. */845return !had_equal || had_value;846}847848static bool849parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)850{851return parse_feature_value_prefix (pp, end, feature) &&852parse_tag (pp, end, &feature->tag) &&853parse_feature_indices (pp, end, feature) &&854parse_feature_value_postfix (pp, end, feature) &&855parse_space (pp, end) &&856*pp == end;857}858859/**860* hb_feature_from_string:861* @str: (array length=len) (element-type uint8_t): a string to parse862* @len: length of @str, or -1 if string is `NULL` terminated863* @feature: (out): the #hb_feature_t to initialize with the parsed values864*865* Parses a string into a #hb_feature_t.866*867* The format for specifying feature strings follows. All valid CSS868* font-feature-settings values other than 'normal' and the global values are869* also accepted, though not documented below. CSS string escapes are not870* supported.871*872* The range indices refer to the positions between Unicode characters. The873* position before the first character is always 0.874*875* The format is Python-esque. Here is how it all works:876*877* <informaltable pgwide='1' align='left' frame='none'>878* <tgroup cols='5'>879* <thead>880* <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>881* </thead>882* <tbody>883* <row><entry>Setting value:</entry></row>884* <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>885* <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>886* <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>887* <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>888* <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>889* <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>890* <row><entry>Setting index:</entry></row>891* <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>892* <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>893* <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>894* <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>895* <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>896* <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>897* <row><entry>Mixing it all:</entry></row>898* <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>899* </tbody>900* </tgroup>901* </informaltable>902*903* Return value:904* `true` if @str is successfully parsed, `false` otherwise905*906* Since: 0.9.5907**/908hb_bool_t909hb_feature_from_string (const char *str, int len,910hb_feature_t *feature)911{912hb_feature_t feat;913914if (len < 0)915len = strlen (str);916917if (likely (parse_one_feature (&str, str + len, &feat)))918{919if (feature)920*feature = feat;921return true;922}923924if (feature)925hb_memset (feature, 0, sizeof (*feature));926return false;927}928929/**930* hb_feature_to_string:931* @feature: an #hb_feature_t to convert932* @buf: (array length=size) (out): output string933* @size: the allocated size of @buf934*935* Converts a #hb_feature_t into a `NULL`-terminated string in the format936* understood by hb_feature_from_string(). The client in responsible for937* allocating big enough size for @buf, 128 bytes is more than enough.938*939* Note that the feature value will be omitted if it is '1', but the940* string won't include any whitespace.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, "%" PRIu32, feature->value));974}975assert (len < ARRAY_LENGTH (s));976len = hb_min (len, size - 1);977hb_memcpy (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)1040hb_memset (variation, 0, sizeof (*variation));1041return false;1042}10431044#ifndef HB_NO_SETLOCALE10451046static inline void free_static_C_locale ();10471048static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,1049hb_C_locale_lazy_loader_t>1050{1051static hb_locale_t create ()1052{1053hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);1054if (!l)1055return l;10561057hb_atexit (free_static_C_locale);10581059return l;1060}1061static void destroy (hb_locale_t l)1062{1063freelocale (l);1064}1065static hb_locale_t get_null ()1066{1067return (hb_locale_t) 0;1068}1069} static_C_locale;10701071static inline1072void free_static_C_locale ()1073{1074static_C_locale.free_instance ();1075}10761077static hb_locale_t1078get_C_locale ()1079{1080return static_C_locale.get_unconst ();1081}10821083#endif10841085/**1086* hb_variation_to_string:1087* @variation: an #hb_variation_t to convert1088* @buf: (array length=size) (out caller-allocates): output string1089* @size: the allocated size of @buf1090*1091* Converts an #hb_variation_t into a `NULL`-terminated string in the format1092* understood by hb_variation_from_string(). The client in responsible for1093* allocating big enough size for @buf, 128 bytes is more than enough.1094*1095* Note that the string won't include any whitespace.1096*1097* Since: 1.4.21098*/1099void1100hb_variation_to_string (hb_variation_t *variation,1101char *buf, unsigned int size)1102{1103if (unlikely (!size)) return;11041105char s[128];1106unsigned int len = 0;1107hb_tag_to_string (variation->tag, s + len);1108len += 4;1109while (len && s[len - 1] == ' ')1110len--;1111s[len++] = '=';11121113hb_locale_t oldlocale HB_UNUSED;1114oldlocale = hb_uselocale (get_C_locale ());1115len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));1116(void) hb_uselocale (oldlocale);11171118assert (len < ARRAY_LENGTH (s));1119len = hb_min (len, size - 1);1120hb_memcpy (buf, s, len);1121buf[len] = '\0';1122}11231124/**1125* hb_color_get_alpha:1126* @color: an #hb_color_t we are interested in its channels.1127*1128* Fetches the alpha channel of the given @color.1129*1130* Return value: Alpha channel value1131*1132* Since: 2.1.01133*/1134uint8_t1135(hb_color_get_alpha) (hb_color_t color)1136{1137return hb_color_get_alpha (color);1138}11391140/**1141* hb_color_get_red:1142* @color: an #hb_color_t we are interested in its channels.1143*1144* Fetches the red channel of the given @color.1145*1146* Return value: Red channel value1147*1148* Since: 2.1.01149*/1150uint8_t1151(hb_color_get_red) (hb_color_t color)1152{1153return hb_color_get_red (color);1154}11551156/**1157* hb_color_get_green:1158* @color: an #hb_color_t we are interested in its channels.1159*1160* Fetches the green channel of the given @color.1161*1162* Return value: Green channel value1163*1164* Since: 2.1.01165*/1166uint8_t1167(hb_color_get_green) (hb_color_t color)1168{1169return hb_color_get_green (color);1170}11711172/**1173* hb_color_get_blue:1174* @color: an #hb_color_t we are interested in its channels.1175*1176* Fetches the blue channel of the given @color.1177*1178* Return value: Blue channel value1179*1180* Since: 2.1.01181*/1182uint8_t1183(hb_color_get_blue) (hb_color_t color)1184{1185return hb_color_get_blue (color);1186}11871188/**1189* hb_malloc:1190* @size: The size of the memory to allocate.1191*1192* Allocates @size bytes of memory, using the allocator set at1193* compile-time. Typically just malloc().1194*1195* Return value: A pointer to the allocated memory.1196*1197* Since: 11.0.01198**/1199void* hb_malloc(size_t size) { return hb_malloc_impl (size); }12001201/**1202* hb_calloc:1203* @nmemb: The number of elements to allocate.1204* @size: The size of each element.1205*1206* Allocates @nmemb elements of @size bytes each, initialized to zero,1207* using the allocator set at compile-time. Typically just calloc().1208*1209* Return value: A pointer to the allocated memory.1210*1211* Since: 11.0.01212**/1213void* hb_calloc(size_t nmemb, size_t size) { return hb_calloc_impl (nmemb, size); }12141215/**1216* hb_realloc:1217* @ptr: The pointer to the memory to reallocate.1218* @size: The new size of the memory.1219*1220* Reallocates the memory pointed to by @ptr to @size bytes, using the1221* allocator set at compile-time. Typically just realloc().1222*1223* Return value: A pointer to the reallocated memory.1224*1225* Since: 11.0.01226**/1227void* hb_realloc(void *ptr, size_t size) { return hb_realloc_impl (ptr, size); }12281229/**1230* hb_free:1231* @ptr: The pointer to the memory to free.1232*1233* Frees the memory pointed to by @ptr, using the allocator set at1234* compile-time. Typically just free().1235*1236* Since: 11.0.01237**/1238void hb_free(void *ptr) { hb_free_impl (ptr); }123912401241/* If there is no visibility control, then hb-static.cc will NOT1242* define anything. Instead, we get it to define one set in here1243* only, so only libharfbuzz.so defines them, not other libs. */1244#ifdef HB_NO_VISIBILITY1245#undef HB_NO_VISIBILITY1246#include "hb-static.cc"1247#define HB_NO_VISIBILITY 11248#endif124912501251