2 * Copyright © 2009,2010 Red Hat, Inc.
3 * Copyright © 2011,2012 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
30 #include "hb-machinery.hh"
34 #ifdef HB_NO_SETLOCALE
35 #define setlocale(Category, Locale) "C"
41 * @short_description: Common data types
44 * Common data types used across HarfBuzz are defined here.
50 hb_atomic_int_t _hb_options;
57 u.opts.initialized = true;
59 const char *c = getenv ("HB_OPTIONS");
64 const char *p = strchr (c, ':');
68 #define OPTION(name, symbol) \
69 if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0)
71 OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
80 /* This is idempotent and threadsafe. */
81 _hb_options.set_relaxed (u.i);
89 * @str: (array length=len) (element-type uint8_t):
99 hb_tag_from_string (const char *str, int len)
104 if (!str || !len || !*str)
107 if (len < 0 || len > 4)
109 for (i = 0; i < (unsigned) len && str[i]; i++)
114 return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
120 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t):
127 hb_tag_to_string (hb_tag_t tag, char *buf)
129 buf[0] = (char) (uint8_t) (tag >> 24);
130 buf[1] = (char) (uint8_t) (tag >> 16);
131 buf[2] = (char) (uint8_t) (tag >> 8);
132 buf[3] = (char) (uint8_t) (tag >> 0);
138 const char direction_strings[][4] = {
146 * hb_direction_from_string:
147 * @str: (array length=len) (element-type uint8_t):
157 hb_direction_from_string (const char *str, int len)
159 if (unlikely (!str || !len || !*str))
160 return HB_DIRECTION_INVALID;
162 /* Lets match loosely: just match the first letter, such that
163 * all of "ltr", "left-to-right", etc work!
165 char c = TOLOWER (str[0]);
166 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
167 if (c == direction_strings[i][0])
168 return (hb_direction_t) (HB_DIRECTION_LTR + i);
170 return HB_DIRECTION_INVALID;
174 * hb_direction_to_string:
179 * Return value: (transfer none):
184 hb_direction_to_string (hb_direction_t direction)
186 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
187 < ARRAY_LENGTH (direction_strings)))
188 return direction_strings[direction - HB_DIRECTION_LTR];
196 struct hb_language_impl_t {
200 static const char canon_map[256] = {
201 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
202 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
203 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
204 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
205 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
206 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
207 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
208 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
212 lang_equal (hb_language_t v1,
215 const unsigned char *p1 = (const unsigned char *) v1;
216 const unsigned char *p2 = (const unsigned char *) v2;
218 while (*p1 && *p1 == canon_map[*p2]) {
223 return *p1 == canon_map[*p2];
228 lang_hash (const void *key)
230 const unsigned char *p = key;
232 while (canon_map[*p])
234 h = (h << 5) - h + canon_map[*p];
243 struct hb_language_item_t {
245 struct hb_language_item_t *next;
248 bool operator == (const char *s) const
249 { return lang_equal (lang, s); }
251 hb_language_item_t & operator = (const char *s) {
252 /* If a custom allocated is used calling strdup() pairs
253 badly with a call to the custom free() in fini() below.
254 Therefore don't call strdup(), implement its behavior.
256 size_t len = strlen(s) + 1;
257 lang = (hb_language_t) malloc(len);
260 memcpy((unsigned char *) lang, s, len);
261 for (unsigned char *p = (unsigned char *) lang; *p; p++)
268 void fini () { free ((void *) lang); }
272 /* Thread-safe lock-free language list */
274 static hb_atomic_ptr_t <hb_language_item_t> langs;
281 hb_language_item_t *first_lang = langs;
282 if (unlikely (!langs.cmpexch (first_lang, nullptr)))
286 hb_language_item_t *next = first_lang->next;
294 static hb_language_item_t *
295 lang_find_or_insert (const char *key)
298 hb_language_item_t *first_lang = langs;
300 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
304 /* Not found; allocate one. */
305 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
306 if (unlikely (!lang))
308 lang->next = first_lang;
310 if (unlikely (!lang->lang))
316 if (unlikely (!langs.cmpexch (first_lang, lang)))
325 atexit (free_langs); /* First person registers atexit() callback. */
333 * hb_language_from_string:
334 * @str: (array length=len) (element-type uint8_t): a string representing
335 * a BCP 47 language tag
336 * @len: length of the @str, or -1 if it is %NULL-terminated.
338 * Converts @str representing a BCP 47 language tag to the corresponding
341 * Return value: (transfer none):
342 * The #hb_language_t corresponding to the BCP 47 language tag.
347 hb_language_from_string (const char *str, int len)
349 if (!str || !len || !*str)
350 return HB_LANGUAGE_INVALID;
352 hb_language_item_t *item = nullptr;
355 /* NUL-terminate it. */
357 len = hb_min (len, (int) sizeof (strbuf) - 1);
358 memcpy (strbuf, str, len);
360 item = lang_find_or_insert (strbuf);
363 item = lang_find_or_insert (str);
365 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
369 * hb_language_to_string:
370 * @language: an #hb_language_t to convert.
372 * See hb_language_from_string().
374 * Return value: (transfer none):
375 * A %NULL-terminated string representing the @language. Must not be freed by
381 hb_language_to_string (hb_language_t language)
383 if (unlikely (!language)) return nullptr;
389 * hb_language_get_default:
391 * Get default language from current locale.
393 * Note that the first time this function is called, it calls
394 * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying
395 * setlocale function is, in many implementations, NOT threadsafe. To avoid
396 * problems, call this function once before multiple threads can call it.
397 * This function is only used from hb_buffer_guess_segment_properties() by
400 * Return value: (transfer none):
405 hb_language_get_default ()
407 static hb_atomic_ptr_t <hb_language_t> default_language;
409 hb_language_t language = default_language;
410 if (unlikely (language == HB_LANGUAGE_INVALID))
412 language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
413 (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
423 * hb_script_from_iso15924_tag:
424 * @tag: an #hb_tag_t representing an ISO 15924 tag.
426 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
429 * An #hb_script_t corresponding to the ISO 15924 tag.
434 hb_script_from_iso15924_tag (hb_tag_t tag)
436 if (unlikely (tag == HB_TAG_NONE))
437 return HB_SCRIPT_INVALID;
439 /* Be lenient, adjust case (one capital letter followed by three small letters) */
440 tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
444 /* These graduated from the 'Q' private-area codes, but
445 * the old code is still aliased by Unicode, and the Qaai
446 * one in use by ICU. */
447 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
448 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
450 /* Script variants from https://unicode.org/iso15924/ */
451 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
452 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
453 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
454 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
455 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
456 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
459 /* If it looks right, just use the tag as a script */
460 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
461 return (hb_script_t) tag;
463 /* Otherwise, return unknown */
464 return HB_SCRIPT_UNKNOWN;
468 * hb_script_from_string:
469 * @str: (array length=len) (element-type uint8_t): a string representing an
471 * @len: length of the @str, or -1 if it is %NULL-terminated.
473 * Converts a string @str representing an ISO 15924 script tag to a
474 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
475 * hb_script_from_iso15924_tag().
478 * An #hb_script_t corresponding to the ISO 15924 tag.
483 hb_script_from_string (const char *str, int len)
485 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
489 * hb_script_to_iso15924_tag:
490 * @script: an #hb_script_t to convert.
492 * See hb_script_from_iso15924_tag().
495 * An #hb_tag_t representing an ISO 15924 script tag.
500 hb_script_to_iso15924_tag (hb_script_t script)
502 return (hb_tag_t) script;
506 * hb_script_get_horizontal_direction:
516 hb_script_get_horizontal_direction (hb_script_t script)
518 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
519 switch ((hb_tag_t) script)
521 /* Unicode-1.1 additions */
522 case HB_SCRIPT_ARABIC:
523 case HB_SCRIPT_HEBREW:
525 /* Unicode-3.0 additions */
526 case HB_SCRIPT_SYRIAC:
527 case HB_SCRIPT_THAANA:
529 /* Unicode-4.0 additions */
530 case HB_SCRIPT_CYPRIOT:
532 /* Unicode-4.1 additions */
533 case HB_SCRIPT_KHAROSHTHI:
535 /* Unicode-5.0 additions */
536 case HB_SCRIPT_PHOENICIAN:
539 /* Unicode-5.1 additions */
540 case HB_SCRIPT_LYDIAN:
542 /* Unicode-5.2 additions */
543 case HB_SCRIPT_AVESTAN:
544 case HB_SCRIPT_IMPERIAL_ARAMAIC:
545 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
546 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
547 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
548 case HB_SCRIPT_OLD_TURKIC:
549 case HB_SCRIPT_SAMARITAN:
551 /* Unicode-6.0 additions */
552 case HB_SCRIPT_MANDAIC:
554 /* Unicode-6.1 additions */
555 case HB_SCRIPT_MEROITIC_CURSIVE:
556 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
558 /* Unicode-7.0 additions */
559 case HB_SCRIPT_MANICHAEAN:
560 case HB_SCRIPT_MENDE_KIKAKUI:
561 case HB_SCRIPT_NABATAEAN:
562 case HB_SCRIPT_OLD_NORTH_ARABIAN:
563 case HB_SCRIPT_PALMYRENE:
564 case HB_SCRIPT_PSALTER_PAHLAVI:
566 /* Unicode-8.0 additions */
567 case HB_SCRIPT_HATRAN:
569 /* Unicode-9.0 additions */
570 case HB_SCRIPT_ADLAM:
572 /* Unicode-11.0 additions */
573 case HB_SCRIPT_HANIFI_ROHINGYA:
574 case HB_SCRIPT_OLD_SOGDIAN:
575 case HB_SCRIPT_SOGDIAN:
577 /* Unicode-12.0 additions */
578 case HB_SCRIPT_ELYMAIC:
580 /* Unicode-13.0 additions */
581 case HB_SCRIPT_CHORASMIAN:
582 case HB_SCRIPT_YEZIDI:
584 return HB_DIRECTION_RTL;
587 /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
588 case HB_SCRIPT_OLD_HUNGARIAN:
589 case HB_SCRIPT_OLD_ITALIC:
590 case HB_SCRIPT_RUNIC:
592 return HB_DIRECTION_INVALID;
595 return HB_DIRECTION_LTR;
605 * @short_description: Information about the version of HarfBuzz in use
608 * These functions and macros allow accessing version of the HarfBuzz
609 * library used at compile- as well as run-time, and to direct code
610 * conditionally based on those versions, again, at compile- or run-time.
616 * @major: (out): Library major version component.
617 * @minor: (out): Library minor version component.
618 * @micro: (out): Library micro version component.
620 * Returns library version as three integer components.
625 hb_version (unsigned int *major,
629 *major = HB_VERSION_MAJOR;
630 *minor = HB_VERSION_MINOR;
631 *micro = HB_VERSION_MICRO;
637 * Returns library version as a string with three components.
639 * Return value: library version string.
646 return HB_VERSION_STRING;
650 * hb_version_atleast:
662 hb_version_atleast (unsigned int major,
666 return HB_VERSION_ATLEAST (major, minor, micro);
671 /* hb_feature_t and hb_variation_t */
674 parse_space (const char **pp, const char *end)
676 while (*pp < end && ISSPACE (**pp))
682 parse_char (const char **pp, const char *end, char c)
684 parse_space (pp, end);
686 if (*pp == end || **pp != c)
694 parse_uint (const char **pp, const char *end, unsigned int *pv)
696 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
697 * such that -1 turns into "big number"... */
699 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
706 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
708 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
709 * such that -1 turns into "big number"... */
711 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
718 parse_bool (const char **pp, const char *end, uint32_t *pv)
720 parse_space (pp, end);
723 while (*pp < end && ISALPHA(**pp))
726 /* CSS allows on/off as aliases 1/0. */
728 && TOLOWER (p[0]) == 'o'
729 && TOLOWER (p[1]) == 'n')
731 else if (*pp - p == 3
732 && TOLOWER (p[0]) == 'o'
733 && TOLOWER (p[1]) == 'f'
734 && TOLOWER (p[2]) == 'f')
745 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
747 if (parse_char (pp, end, '-'))
750 parse_char (pp, end, '+');
758 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
760 parse_space (pp, end);
764 if (*pp < end && (**pp == '\'' || **pp == '"'))
771 while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
774 if (p == *pp || *pp - p > 4)
777 *tag = hb_tag_from_string (p, *pp - p);
781 /* CSS expects exactly four bytes. And we only allow quotations for
782 * CSS compatibility. So, enforce the length. */
785 if (*pp == end || **pp != quote)
794 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
796 parse_space (pp, end);
800 feature->start = HB_FEATURE_GLOBAL_START;
801 feature->end = HB_FEATURE_GLOBAL_END;
803 if (!parse_char (pp, end, '['))
806 has_start = parse_uint (pp, end, &feature->start);
808 if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
809 parse_uint (pp, end, &feature->end);
812 feature->end = feature->start + 1;
815 return parse_char (pp, end, ']');
819 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
821 bool had_equal = parse_char (pp, end, '=');
822 bool had_value = parse_uint32 (pp, end, &feature->value) ||
823 parse_bool (pp, end, &feature->value);
824 /* CSS doesn't use equal-sign between tag and value.
825 * If there was an equal-sign, then there *must* be a value.
826 * A value without an equal-sign is ok, but not required. */
827 return !had_equal || had_value;
831 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
833 return parse_feature_value_prefix (pp, end, feature) &&
834 parse_tag (pp, end, &feature->tag) &&
835 parse_feature_indices (pp, end, feature) &&
836 parse_feature_value_postfix (pp, end, feature) &&
837 parse_space (pp, end) &&
842 * hb_feature_from_string:
843 * @str: (array length=len) (element-type uint8_t): a string to parse
844 * @len: length of @str, or -1 if string is %NULL terminated
845 * @feature: (out): the #hb_feature_t to initialize with the parsed values
847 * Parses a string into a #hb_feature_t.
849 * The format for specifying feature strings follows. All valid CSS
850 * font-feature-settings values other than 'normal' and the global values are
851 * also accepted, though not documented below. CSS string escapes are not
854 * The range indices refer to the positions between Unicode characters. The
855 * position before the first character is always 0.
857 * The format is Python-esque. Here is how it all works:
859 * <informaltable pgwide='1' align='left' frame='none'>
862 * <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
865 * <row><entry>Setting value:</entry></row>
866 * <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
867 * <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
868 * <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
869 * <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
870 * <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
871 * <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>
872 * <row><entry>Setting index:</entry></row>
873 * <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
874 * <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
875 * <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>
876 * <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>
877 * <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>
878 * <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
879 * <row><entry>Mixing it all:</entry></row>
880 * <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>
886 * %true if @str is successfully parsed, %false otherwise.
891 hb_feature_from_string (const char *str, int len,
892 hb_feature_t *feature)
899 if (likely (parse_one_feature (&str, str + len, &feat)))
907 memset (feature, 0, sizeof (*feature));
912 * hb_feature_to_string:
913 * @feature: an #hb_feature_t to convert
914 * @buf: (array length=size) (out): output string
915 * @size: the allocated size of @buf
917 * Converts a #hb_feature_t into a %NULL-terminated string in the format
918 * understood by hb_feature_from_string(). The client in responsible for
919 * allocating big enough size for @buf, 128 bytes is more than enough.
924 hb_feature_to_string (hb_feature_t *feature,
925 char *buf, unsigned int size)
927 if (unlikely (!size)) return;
930 unsigned int len = 0;
931 if (feature->value == 0)
933 hb_tag_to_string (feature->tag, s + len);
935 while (len && s[len - 1] == ' ')
937 if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
941 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
942 if (feature->end != feature->start + 1) {
944 if (feature->end != HB_FEATURE_GLOBAL_END)
945 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
949 if (feature->value > 1)
952 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
954 assert (len < ARRAY_LENGTH (s));
955 len = hb_min (len, size - 1);
956 memcpy (buf, s, len);
963 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
965 parse_char (pp, end, '='); /* Optional. */
967 if (unlikely (!hb_parse_double (pp, end, &v))) return false;
969 variation->value = v;
974 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
976 return parse_tag (pp, end, &variation->tag) &&
977 parse_variation_value (pp, end, variation) &&
978 parse_space (pp, end) &&
983 * hb_variation_from_string:
988 hb_variation_from_string (const char *str, int len,
989 hb_variation_t *variation)
996 if (likely (parse_one_variation (&str, str + len, &var)))
1004 memset (variation, 0, sizeof (*variation));
1009 * hb_variation_to_string:
1014 hb_variation_to_string (hb_variation_t *variation,
1015 char *buf, unsigned int size)
1017 if (unlikely (!size)) return;
1020 unsigned int len = 0;
1021 hb_tag_to_string (variation->tag, s + len);
1023 while (len && s[len - 1] == ' ')
1026 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
1028 assert (len < ARRAY_LENGTH (s));
1029 len = hb_min (len, size - 1);
1030 memcpy (buf, s, len);
1035 * hb_color_get_alpha:
1036 * color: a #hb_color_t we are interested in its channels.
1038 * Return value: Alpha channel value of the given color
1043 (hb_color_get_alpha) (hb_color_t color)
1045 return hb_color_get_alpha (color);
1050 * color: a #hb_color_t we are interested in its channels.
1052 * Return value: Red channel value of the given color
1057 (hb_color_get_red) (hb_color_t color)
1059 return hb_color_get_red (color);
1063 * hb_color_get_green:
1064 * color: a #hb_color_t we are interested in its channels.
1066 * Return value: Green channel value of the given color
1071 (hb_color_get_green) (hb_color_t color)
1073 return hb_color_get_green (color);
1077 * hb_color_get_blue:
1078 * color: a #hb_color_t we are interested in its channels.
1080 * Return value: Blue channel value of the given color
1085 (hb_color_get_blue) (hb_color_t color)
1087 return hb_color_get_blue (color);
1091 /* If there is no visibility control, then hb-static.cc will NOT
1092 * define anything. Instead, we get it to define one set in here
1093 * only, so only libharfbuzz.so defines them, not other libs. */
1094 #ifdef HB_NO_VISIBILITY
1095 #undef HB_NO_VISIBILITY
1096 #include "hb-static.cc"
1097 #define HB_NO_VISIBILITY 1