blob: bd4cd88e34f9aebac48a20a71379a293378b814c [file] [log] [blame]
Behdad Esfahbod83f34672010-05-21 13:43:49 +01001/*
Behdad Esfahbod2409d5f2011-04-21 17:14:28 -04002 * Copyright © 2009,2010 Red Hat, Inc.
Behdad Esfahbodcad38212012-03-07 17:13:25 -05003 * Copyright © 2011,2012 Google, Inc.
Behdad Esfahbod83f34672010-05-21 13:43:49 +01004 *
5 * This is part of HarfBuzz, a text shaping library.
6 *
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.
12 *
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
17 * DAMAGE.
18 *
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.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
Behdad Esfahbod2409d5f2011-04-21 17:14:28 -040026 * Google Author(s): Behdad Esfahbod
Behdad Esfahbod83f34672010-05-21 13:43:49 +010027 */
28
Behdad Esfahbodc77ae402018-08-25 22:36:36 -070029#include "hb.hh"
Behdad Esfahbodc77ae402018-08-25 22:36:36 -070030#include "hb-machinery.hh"
Behdad Esfahbodb8d61832011-05-05 15:14:04 -040031
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -040032
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -070033/**
34 * SECTION:hb-common
Behdad Esfahbodcf5fa572018-10-27 04:50:38 -070035 * @title: hb-common
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -070036 * @short_description: Common data types
37 * @include: hb.h
38 *
39 * Common data types used across HarfBuzz are defined here.
40 **/
41
42
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050043/* hb_options_t */
44
Behdad Esfahbod4bc16ac2018-07-31 21:05:51 -070045hb_atomic_int_t _hb_options;
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050046
47void
Ebrahim Byagowie4120082018-12-17 21:31:01 +033048_hb_options_init ()
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050049{
50 hb_options_union_t u;
51 u.i = 0;
Bruce Mitchener8d1e4792018-10-18 22:18:42 +070052 u.opts.initialized = true;
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050053
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040054 const char *c = getenv ("HB_OPTIONS");
55 if (c)
56 {
57 while (*c)
58 {
59 const char *p = strchr (c, ':');
60 if (!p)
Ebrahim Byagowia0b4ac42019-08-24 17:57:14 +043061 p = c + strlen (c);
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040062
63#define OPTION(name, symbol) \
GaryQianccf14482019-06-24 12:57:13 -070064 if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0)
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040065
66 OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040067
68#undef OPTION
69
70 c = *p ? p + 1 : p;
71 }
72
73 }
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050074
75 /* This is idempotent and threadsafe. */
Behdad Esfahbodf73c15c2022-08-03 12:54:03 -060076 _hb_options = u.i;
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050077}
78
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -040079
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -040080/* hb_tag_t */
81
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040082/**
83 * hb_tag_from_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +010084 * @str: (array length=len) (element-type uint8_t): String to convert
Khaled Hosny98e90cc2022-06-30 08:43:57 +020085 * @len: Length of @str, or -1 if it is `NULL`-terminated
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040086 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +010087 * Converts a string into an #hb_tag_t. Valid tags
88 * are four characters. Shorter input strings will be
89 * padded with spaces. Longer input strings will be
90 * truncated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040091 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +010092 * Return value: The #hb_tag_t corresponding to @str
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040093 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +043094 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040095 **/
Behdad Esfahbod83f34672010-05-21 13:43:49 +010096hb_tag_t
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040097hb_tag_from_string (const char *str, int len)
Behdad Esfahbod83f34672010-05-21 13:43:49 +010098{
99 char tag[4];
100 unsigned int i;
101
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400102 if (!str || !len || !*str)
Behdad Esfahbod7ff74012011-04-11 13:27:30 -0400103 return HB_TAG_NONE;
104
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200105 if (len < 0 || len > 4)
106 len = 4;
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400107 for (i = 0; i < (unsigned) len && str[i]; i++)
108 tag[i] = str[i];
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100109 for (; i < 4; i++)
110 tag[i] = ' ';
111
Behdad Esfahbod8eaff982017-10-31 15:30:06 -0600112 return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100113}
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -0400114
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400115/**
116 * hb_tag_to_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100117 * @tag: #hb_tag_t to convert
118 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400119 *
Behdad Esfahboda45a6302022-01-08 15:47:33 -0800120 * Converts an #hb_tag_t to a string and returns it in @buf.
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100121 * Strings will be four characters long.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400122 *
Sascha Brawer01c3a882015-06-01 13:22:01 +0200123 * Since: 0.9.5
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400124 **/
Behdad Esfahbode30ebd22012-09-06 22:09:06 -0400125void
126hb_tag_to_string (hb_tag_t tag, char *buf)
127{
128 buf[0] = (char) (uint8_t) (tag >> 24);
129 buf[1] = (char) (uint8_t) (tag >> 16);
130 buf[2] = (char) (uint8_t) (tag >> 8);
131 buf[3] = (char) (uint8_t) (tag >> 0);
132}
133
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -0400134
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400135/* hb_direction_t */
136
Behdad Esfahbodc859cbf2022-06-16 13:55:12 -0600137static const char direction_strings[][4] = {
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400138 "ltr",
139 "rtl",
140 "ttb",
141 "btt"
142};
143
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400144/**
145 * hb_direction_from_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100146 * @str: (array length=len) (element-type uint8_t): String to convert
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200147 * @len: Length of @str, or -1 if it is `NULL`-terminated
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400148 *
Behdad Esfahboda45a6302022-01-08 15:47:33 -0800149 * Converts a string to an #hb_direction_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400150 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100151 * Matching is loose and applies only to the first letter. For
152 * examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430153 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100154 * Unmatched strings will return #HB_DIRECTION_INVALID.
Behdad Esfahboda45a6302022-01-08 15:47:33 -0800155 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100156 * Return value: The #hb_direction_t matching @str
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400157 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430158 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400159 **/
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400160hb_direction_t
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200161hb_direction_from_string (const char *str, int len)
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400162{
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200163 if (unlikely (!str || !len || !*str))
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400164 return HB_DIRECTION_INVALID;
165
166 /* Lets match loosely: just match the first letter, such that
167 * all of "ltr", "left-to-right", etc work!
168 */
169 char c = TOLOWER (str[0]);
170 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
171 if (c == direction_strings[i][0])
Behdad Esfahbod4bf90f62012-04-12 17:38:23 -0400172 return (hb_direction_t) (HB_DIRECTION_LTR + i);
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400173
174 return HB_DIRECTION_INVALID;
175}
176
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400177/**
178 * hb_direction_to_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100179 * @direction: The #hb_direction_t to convert
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400180 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100181 * Converts an #hb_direction_t to a string.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400182 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100183 * Return value: (transfer none): The string corresponding to @direction
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400184 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430185 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400186 **/
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400187const char *
188hb_direction_to_string (hb_direction_t direction)
189{
Behdad Esfahbod4bf90f62012-04-12 17:38:23 -0400190 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
191 < ARRAY_LENGTH (direction_strings)))
192 return direction_strings[direction - HB_DIRECTION_LTR];
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400193
194 return "invalid";
195}
196
197
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400198/* hb_language_t */
199
Behdad Esfahbod1bc1cb32012-06-16 15:21:55 -0400200struct hb_language_impl_t {
Behdad Esfahbod3cbdf702011-04-15 12:32:06 -0400201 const char s[1];
202};
203
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400204static const char canon_map[256] = {
205 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
206 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
208 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
David Corbett018ba462018-11-23 13:21:22 -0500209 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400210 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
211 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
212 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
213};
214
Behdad Esfahbodf3b170b2015-04-08 16:26:24 -0700215static bool
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400216lang_equal (hb_language_t v1,
217 const void *v2)
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400218{
Behdad Esfahbodc57d4542011-04-20 18:50:27 -0400219 const unsigned char *p1 = (const unsigned char *) v1;
220 const unsigned char *p2 = (const unsigned char *) v2;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400221
Chris Petersonaacca372017-04-17 23:25:24 -0700222 while (*p1 && *p1 == canon_map[*p2]) {
223 p1++;
224 p2++;
225 }
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400226
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400227 return *p1 == canon_map[*p2];
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400228}
229
230#if 0
231static unsigned int
232lang_hash (const void *key)
233{
234 const unsigned char *p = key;
235 unsigned int h = 0;
236 while (canon_map[*p])
237 {
238 h = (h << 5) - h + canon_map[*p];
239 p++;
240 }
241
242 return h;
243}
244#endif
245
246
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400247struct hb_language_item_t {
248
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400249 struct hb_language_item_t *next;
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400250 hb_language_t lang;
251
Ebrahim Byagowib2ebaa92018-12-16 22:38:10 +0330252 bool operator == (const char *s) const
253 { return lang_equal (lang, s); }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400254
Behdad Esfahbodbb48bf52021-07-08 10:53:45 -0600255 hb_language_item_t & operator = (const char *s)
256 {
257 /* We can't call strdup(), because we allow custom allocators. */
Sebastian Rasmussen92e2c4b2017-05-29 12:53:30 -0500258 size_t len = strlen(s) + 1;
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600259 lang = (hb_language_t) hb_malloc(len);
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400260 if (likely (lang))
261 {
Behdad Esfahbod59c45f62022-11-22 12:54:50 -0700262 hb_memcpy((unsigned char *) lang, s, len);
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400263 for (unsigned char *p = (unsigned char *) lang; *p; p++)
264 *p = canon_map[*p];
265 }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400266
267 return *this;
268 }
269
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600270 void fini () { hb_free ((void *) lang); }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400271};
272
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400273
Behdad Esfahbodbb48bf52021-07-08 10:53:45 -0600274/* Thread-safe lockfree language list */
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400275
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700276static hb_atomic_ptr_t <hb_language_item_t> langs;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400277
Behdad Esfahboded116322021-09-14 07:09:54 -0400278static inline void
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330279free_langs ()
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400280{
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700281retry:
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500282 hb_language_item_t *first_lang = langs;
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700283 if (unlikely (!langs.cmpexch (first_lang, nullptr)))
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700284 goto retry;
285
286 while (first_lang) {
287 hb_language_item_t *next = first_lang->next;
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400288 first_lang->fini ();
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600289 hb_free (first_lang);
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700290 first_lang = next;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400291 }
292}
293
294static hb_language_item_t *
295lang_find_or_insert (const char *key)
296{
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400297retry:
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500298 hb_language_item_t *first_lang = langs;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400299
300 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
301 if (*lang == key)
302 return lang;
303
304 /* Not found; allocate one. */
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600305 hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400306 if (unlikely (!lang))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200307 return nullptr;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400308 lang->next = first_lang;
309 *lang = key;
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400310 if (unlikely (!lang->lang))
311 {
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600312 hb_free (lang);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200313 return nullptr;
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400314 }
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400315
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700316 if (unlikely (!langs.cmpexch (first_lang, lang)))
317 {
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400318 lang->fini ();
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600319 hb_free (lang);
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400320 goto retry;
321 }
322
Behdad Esfahbod04aed572012-06-05 18:30:19 -0400323 if (!first_lang)
Behdad Esfahboded116322021-09-14 07:09:54 -0400324 hb_atexit (free_langs); /* First person registers atexit() callback. */
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400325
326 return lang;
327}
328
Behdad Esfahbodb8d61832011-05-05 15:14:04 -0400329
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400330/**
331 * hb_language_from_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400332 * @str: (array length=len) (element-type uint8_t): a string representing
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200333 * a BCP 47 language tag
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200334 * @len: length of the @str, or -1 if it is `NULL`-terminated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400335 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200336 * Converts @str representing a BCP 47 language tag to the corresponding
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400337 * #hb_language_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400338 *
Khaled Hosny04f89e82015-04-10 17:49:01 +0200339 * Return value: (transfer none):
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200340 * The #hb_language_t corresponding to the BCP 47 language tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400341 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430342 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400343 **/
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400344hb_language_t
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200345hb_language_from_string (const char *str, int len)
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400346{
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200347 if (!str || !len || !*str)
Behdad Esfahbod1a64f6e2011-05-13 22:55:32 -0400348 return HB_LANGUAGE_INVALID;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400349
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200350 hb_language_item_t *item = nullptr;
Behdad Esfahbod48360ec2013-09-26 16:48:42 -0400351 if (len >= 0)
352 {
Behdad Esfahboddac86022014-06-03 17:57:00 -0400353 /* NUL-terminate it. */
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100354 char strbuf[64];
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700355 len = hb_min (len, (int) sizeof (strbuf) - 1);
Behdad Esfahbod59c45f62022-11-22 12:54:50 -0700356 hb_memcpy (strbuf, str, len);
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200357 strbuf[len] = '\0';
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100358 item = lang_find_or_insert (strbuf);
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200359 }
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100360 else
361 item = lang_find_or_insert (str);
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400362
Behdad Esfahbod1a64f6e2011-05-13 22:55:32 -0400363 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400364}
365
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400366/**
367 * hb_language_to_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100368 * @language: The #hb_language_t to convert
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400369 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100370 * Converts an #hb_language_t to a string.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400371 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400372 * Return value: (transfer none):
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200373 * A `NULL`-terminated string representing the @language. Must not be freed by
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400374 * the caller.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400375 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430376 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400377 **/
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400378const char *
379hb_language_to_string (hb_language_t language)
380{
Ebrahim Byagowie6909ee2019-09-18 22:12:25 +0430381 if (unlikely (!language)) return nullptr;
Ebrahim Byagowid8af9ee2019-09-18 00:47:55 +0430382
Behdad Esfahbod3cbdf702011-04-15 12:32:06 -0400383 return language->s;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400384}
385
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400386/**
387 * hb_language_get_default:
388 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100389 * Fetch the default language from current locale.
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430390 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100391 * <note>Note that the first time this function is called, it calls
Behdad Esfahbodba0f0f12018-09-30 03:49:52 -0400392 * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying
393 * setlocale function is, in many implementations, NOT threadsafe. To avoid
394 * problems, call this function once before multiple threads can call it.
395 * This function is only used from hb_buffer_guess_segment_properties() by
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100396 * HarfBuzz itself.</note>
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400397 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100398 * Return value: (transfer none): The default language of the locale as
399 * an #hb_language_t
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400400 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430401 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400402 **/
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400403hb_language_t
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330404hb_language_get_default ()
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400405{
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700406 static hb_atomic_ptr_t <hb_language_t> default_language;
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400407
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500408 hb_language_t language = default_language;
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700409 if (unlikely (language == HB_LANGUAGE_INVALID))
410 {
Behdad Esfahbodb97e4f72022-01-15 17:47:51 -0700411 language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1);
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700412 (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400413 }
414
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700415 return language;
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400416}
417
Behdad Esfahbodf7f6d272022-07-17 22:15:42 -0600418/**
419 * hb_language_matches:
420 * @language: The #hb_language_t to work on
421 * @specific: Another #hb_language_t
422 *
423 * Check whether a second language tag is the same or a more
424 * specific version of the provided language tag. For example,
425 * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR".
426 *
427 * Return value: `true` if languages match, `false` otherwise.
428 *
Khaled Hosny40b21ed2022-07-23 16:45:32 +0200429 * Since: 5.0.0
Behdad Esfahbodf7f6d272022-07-17 22:15:42 -0600430 **/
431hb_bool_t
432hb_language_matches (hb_language_t language,
433 hb_language_t specific)
434{
435 if (language == specific) return true;
436 if (!language || !specific) return false;
437
438 const char *l = language->s;
439 const char *s = specific->s;
440 unsigned ll = strlen (l);
441 unsigned sl = strlen (s);
442
443 if (ll > sl)
444 return false;
445
446 return strncmp (l, s, ll) == 0 &&
447 (s[ll] == '\0' || s[ll] == '-');
448}
449
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400450
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400451/* hb_script_t */
452
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400453/**
454 * hb_script_from_iso15924_tag:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200455 * @tag: an #hb_tag_t representing an ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400456 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200457 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400458 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430459 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200460 * An #hb_script_t corresponding to the ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400461 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430462 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400463 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400464hb_script_t
465hb_script_from_iso15924_tag (hb_tag_t tag)
466{
Behdad Esfahbodf144a8e2011-04-20 02:54:42 -0400467 if (unlikely (tag == HB_TAG_NONE))
468 return HB_SCRIPT_INVALID;
469
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400470 /* Be lenient, adjust case (one capital letter followed by three small letters) */
Behdad Esfahbod76271002014-07-11 14:54:42 -0400471 tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400472
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400473 switch (tag) {
Behdad Esfahbodd02985e2011-05-02 12:35:14 -0400474
475 /* These graduated from the 'Q' private-area codes, but
476 * the old code is still aliased by Unicode, and the Qaai
477 * one in use by ICU. */
478 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
479 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
480
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +0430481 /* Script variants from https://unicode.org/iso15924/ */
David Corbett3bd43bd2020-11-16 21:55:02 -0500482 case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400483 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
David Corbett3bd43bd2020-11-16 21:55:02 -0500484 case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;
485 case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;
486 case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;
487 case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400488 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
489 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
490 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
491 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
492 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
493 }
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400494
495 /* If it looks right, just use the tag as a script */
Behdad Esfahbod76271002014-07-11 14:54:42 -0400496 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400497 return (hb_script_t) tag;
498
499 /* Otherwise, return unknown */
500 return HB_SCRIPT_UNKNOWN;
501}
502
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400503/**
504 * hb_script_from_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400505 * @str: (array length=len) (element-type uint8_t): a string representing an
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200506 * ISO 15924 tag.
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200507 * @len: length of the @str, or -1 if it is `NULL`-terminated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400508 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200509 * Converts a string @str representing an ISO 15924 script tag to a
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400510 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
511 * hb_script_from_iso15924_tag().
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400512 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430513 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200514 * An #hb_script_t corresponding to the ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400515 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430516 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400517 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400518hb_script_t
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400519hb_script_from_string (const char *str, int len)
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400520{
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400521 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400522}
523
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400524/**
525 * hb_script_to_iso15924_tag:
David Corbett5daeff32019-04-17 09:11:44 -0400526 * @script: an #hb_script_t to convert.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400527 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100528 * Converts an #hb_script_t to a corresponding ISO 15924 script tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400529 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400530 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200531 * An #hb_tag_t representing an ISO 15924 script tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400532 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430533 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400534 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400535hb_tag_t
536hb_script_to_iso15924_tag (hb_script_t script)
537{
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400538 return (hb_tag_t) script;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400539}
540
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400541/**
542 * hb_script_get_horizontal_direction:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100543 * @script: The #hb_script_t to query
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400544 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100545 * Fetches the #hb_direction_t of a script when it is
546 * set horizontally. All right-to-left scripts will return
547 * #HB_DIRECTION_RTL. All left-to-right scripts will return
Khaled Hosny8586f152020-12-24 22:23:47 +0200548 * #HB_DIRECTION_LTR. Scripts that can be written either
549 * horizontally or vertically will return #HB_DIRECTION_INVALID.
550 * Unknown scripts will return #HB_DIRECTION_LTR.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400551 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100552 * Return value: The horizontal #hb_direction_t of @script
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400553 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430554 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400555 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400556hb_direction_t
557hb_script_get_horizontal_direction (hb_script_t script)
558{
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +0430559 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400560 switch ((hb_tag_t) script)
561 {
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500562 /* Unicode-1.1 additions */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400563 case HB_SCRIPT_ARABIC:
564 case HB_SCRIPT_HEBREW:
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500565
566 /* Unicode-3.0 additions */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400567 case HB_SCRIPT_SYRIAC:
568 case HB_SCRIPT_THAANA:
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400569
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400570 /* Unicode-4.0 additions */
571 case HB_SCRIPT_CYPRIOT:
572
Behdad Esfahbod50e810c2012-03-07 12:49:08 -0500573 /* Unicode-4.1 additions */
574 case HB_SCRIPT_KHAROSHTHI:
575
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400576 /* Unicode-5.0 additions */
577 case HB_SCRIPT_PHOENICIAN:
578 case HB_SCRIPT_NKO:
579
Behdad Esfahbod50e810c2012-03-07 12:49:08 -0500580 /* Unicode-5.1 additions */
581 case HB_SCRIPT_LYDIAN:
582
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400583 /* Unicode-5.2 additions */
584 case HB_SCRIPT_AVESTAN:
585 case HB_SCRIPT_IMPERIAL_ARAMAIC:
586 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
587 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
588 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
589 case HB_SCRIPT_OLD_TURKIC:
590 case HB_SCRIPT_SAMARITAN:
591
592 /* Unicode-6.0 additions */
593 case HB_SCRIPT_MANDAIC:
594
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500595 /* Unicode-6.1 additions */
596 case HB_SCRIPT_MEROITIC_CURSIVE:
597 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
598
Behdad Esfahboda4a78992014-04-28 15:06:42 -0700599 /* Unicode-7.0 additions */
600 case HB_SCRIPT_MANICHAEAN:
601 case HB_SCRIPT_MENDE_KIKAKUI:
602 case HB_SCRIPT_NABATAEAN:
603 case HB_SCRIPT_OLD_NORTH_ARABIAN:
604 case HB_SCRIPT_PALMYRENE:
605 case HB_SCRIPT_PSALTER_PAHLAVI:
606
Behdad Esfahbod64a27262015-07-15 01:36:39 +0100607 /* Unicode-8.0 additions */
Cosimo Lupoc8f2a4f2018-01-18 22:49:40 +0100608 case HB_SCRIPT_HATRAN:
Behdad Esfahbod64a27262015-07-15 01:36:39 +0100609
Behdad Esfahbod691086f2016-05-06 12:08:18 +0100610 /* Unicode-9.0 additions */
611 case HB_SCRIPT_ADLAM:
612
Behdad Esfahbod060e6b42018-06-05 17:31:46 -0700613 /* Unicode-11.0 additions */
614 case HB_SCRIPT_HANIFI_ROHINGYA:
615 case HB_SCRIPT_OLD_SOGDIAN:
616 case HB_SCRIPT_SOGDIAN:
617
David Corbett665483c2020-04-01 17:28:12 -0400618 /* Unicode-12.0 additions */
619 case HB_SCRIPT_ELYMAIC:
620
David Corbettfd748fa2020-03-15 15:59:31 -0400621 /* Unicode-13.0 additions */
622 case HB_SCRIPT_CHORASMIAN:
623 case HB_SCRIPT_YEZIDI:
624
David Corbett7b05eec2021-09-14 17:51:11 -0400625 /* Unicode-14.0 additions */
626 case HB_SCRIPT_OLD_UYGHUR:
627
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400628 return HB_DIRECTION_RTL;
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700629
630
631 /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
David Corbettd8d1e7d2018-09-17 11:09:51 -0400632 case HB_SCRIPT_OLD_HUNGARIAN:
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700633 case HB_SCRIPT_OLD_ITALIC:
David Corbett46d8f0d2018-07-06 15:47:03 -0400634 case HB_SCRIPT_RUNIC:
Simon Cozens2d863462023-03-10 14:32:39 +0000635 case HB_SCRIPT_TIFINAGH:
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700636
637 return HB_DIRECTION_INVALID;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400638 }
639
640 return HB_DIRECTION_LTR;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400641}
642
643
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400644/* hb_version */
645
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -0700646
647/**
648 * SECTION:hb-version
Behdad Esfahbodcf5fa572018-10-27 04:50:38 -0700649 * @title: hb-version
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -0700650 * @short_description: Information about the version of HarfBuzz in use
651 * @include: hb.h
652 *
653 * These functions and macros allow accessing version of the HarfBuzz
654 * library used at compile- as well as run-time, and to direct code
655 * conditionally based on those versions, again, at compile- or run-time.
656 **/
657
658
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400659/**
660 * hb_version:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100661 * @major: (out): Library major version component
662 * @minor: (out): Library minor version component
663 * @micro: (out): Library micro version component
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400664 *
665 * Returns library version as three integer components.
666 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430667 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400668 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400669void
670hb_version (unsigned int *major,
671 unsigned int *minor,
672 unsigned int *micro)
673{
674 *major = HB_VERSION_MAJOR;
675 *minor = HB_VERSION_MINOR;
676 *micro = HB_VERSION_MICRO;
677}
678
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400679/**
680 * hb_version_string:
681 *
682 * Returns library version as a string with three components.
683 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100684 * Return value: Library version string
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400685 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430686 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400687 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400688const char *
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330689hb_version_string ()
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400690{
691 return HB_VERSION_STRING;
692}
693
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400694/**
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400695 * hb_version_atleast:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100696 * @major: Library major version component
697 * @minor: Library minor version component
698 * @micro: Library micro version component
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400699 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100700 * Tests the library version against a minimum value,
701 * as three integer components.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400702 *
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200703 * Return value: `true` if the library is equal to or greater than
704 * the test value, `false` otherwise
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400705 *
Sascha Brawer01c3a882015-06-01 13:22:01 +0200706 * Since: 0.9.30
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400707 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400708hb_bool_t
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400709hb_version_atleast (unsigned int major,
710 unsigned int minor,
711 unsigned int micro)
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400712{
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400713 return HB_VERSION_ATLEAST (major, minor, micro);
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400714}
Behdad Esfahbod72364102017-01-20 20:16:53 -0800715
716
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800717
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800718/* hb_feature_t and hb_variation_t */
Behdad Esfahbod72364102017-01-20 20:16:53 -0800719
720static bool
721parse_space (const char **pp, const char *end)
722{
723 while (*pp < end && ISSPACE (**pp))
724 (*pp)++;
725 return true;
726}
727
728static bool
729parse_char (const char **pp, const char *end, char c)
730{
731 parse_space (pp, end);
732
733 if (*pp == end || **pp != c)
734 return false;
735
736 (*pp)++;
737 return true;
738}
739
740static bool
741parse_uint (const char **pp, const char *end, unsigned int *pv)
742{
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430743 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
744 * such that -1 turns into "big number"... */
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430745 int v;
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430746 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800747
748 *pv = v;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800749 return true;
750}
751
752static bool
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430753parse_uint32 (const char **pp, const char *end, uint32_t *pv)
754{
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430755 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
756 * such that -1 turns into "big number"... */
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430757 int v;
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430758 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430759
760 *pv = v;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800761 return true;
762}
763
764static bool
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430765parse_bool (const char **pp, const char *end, uint32_t *pv)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800766{
767 parse_space (pp, end);
768
769 const char *p = *pp;
770 while (*pp < end && ISALPHA(**pp))
771 (*pp)++;
772
773 /* CSS allows on/off as aliases 1/0. */
David Corbett45adc182019-02-18 22:30:40 -0500774 if (*pp - p == 2
775 && TOLOWER (p[0]) == 'o'
776 && TOLOWER (p[1]) == 'n')
Behdad Esfahbod72364102017-01-20 20:16:53 -0800777 *pv = 1;
David Corbett45adc182019-02-18 22:30:40 -0500778 else if (*pp - p == 3
779 && TOLOWER (p[0]) == 'o'
780 && TOLOWER (p[1]) == 'f'
781 && TOLOWER (p[2]) == 'f')
Behdad Esfahbod72364102017-01-20 20:16:53 -0800782 *pv = 0;
783 else
784 return false;
785
786 return true;
787}
788
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800789/* hb_feature_t */
790
Behdad Esfahbod72364102017-01-20 20:16:53 -0800791static bool
792parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
793{
794 if (parse_char (pp, end, '-'))
795 feature->value = 0;
796 else {
797 parse_char (pp, end, '+');
798 feature->value = 1;
799 }
800
801 return true;
802}
803
804static bool
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800805parse_tag (const char **pp, const char *end, hb_tag_t *tag)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800806{
807 parse_space (pp, end);
808
809 char quote = 0;
810
811 if (*pp < end && (**pp == '\'' || **pp == '"'))
812 {
813 quote = **pp;
814 (*pp)++;
815 }
816
817 const char *p = *pp;
Behdad Esfahbode8eb1dc2023-08-09 15:35:59 -0600818 while (*pp < end && (**pp != ' ' && **pp != '=' && **pp != '['))
Behdad Esfahbod72364102017-01-20 20:16:53 -0800819 (*pp)++;
820
821 if (p == *pp || *pp - p > 4)
822 return false;
823
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800824 *tag = hb_tag_from_string (p, *pp - p);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800825
826 if (quote)
827 {
828 /* CSS expects exactly four bytes. And we only allow quotations for
829 * CSS compatibility. So, enforce the length. */
830 if (*pp - p != 4)
831 return false;
832 if (*pp == end || **pp != quote)
833 return false;
834 (*pp)++;
835 }
836
837 return true;
838}
839
840static bool
841parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
842{
843 parse_space (pp, end);
844
845 bool has_start;
846
Behdad Esfahbodbecd84a2018-09-11 01:26:18 +0200847 feature->start = HB_FEATURE_GLOBAL_START;
848 feature->end = HB_FEATURE_GLOBAL_END;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800849
850 if (!parse_char (pp, end, '['))
851 return true;
852
853 has_start = parse_uint (pp, end, &feature->start);
854
Behdad Esfahbod88694362018-10-23 03:07:48 -0700855 if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
Behdad Esfahbod72364102017-01-20 20:16:53 -0800856 parse_uint (pp, end, &feature->end);
857 } else {
858 if (has_start)
859 feature->end = feature->start + 1;
860 }
861
862 return parse_char (pp, end, ']');
863}
864
865static bool
866parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
867{
868 bool had_equal = parse_char (pp, end, '=');
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430869 bool had_value = parse_uint32 (pp, end, &feature->value) ||
Ebrahim Byagowia0b4ac42019-08-24 17:57:14 +0430870 parse_bool (pp, end, &feature->value);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800871 /* CSS doesn't use equal-sign between tag and value.
872 * If there was an equal-sign, then there *must* be a value.
Bruce Mitchener90218fa2018-01-31 20:44:45 +0700873 * A value without an equal-sign is ok, but not required. */
Behdad Esfahbod72364102017-01-20 20:16:53 -0800874 return !had_equal || had_value;
875}
876
Behdad Esfahbod72364102017-01-20 20:16:53 -0800877static bool
878parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
879{
880 return parse_feature_value_prefix (pp, end, feature) &&
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800881 parse_tag (pp, end, &feature->tag) &&
Behdad Esfahbod72364102017-01-20 20:16:53 -0800882 parse_feature_indices (pp, end, feature) &&
883 parse_feature_value_postfix (pp, end, feature) &&
884 parse_space (pp, end) &&
885 *pp == end;
886}
887
888/**
889 * hb_feature_from_string:
890 * @str: (array length=len) (element-type uint8_t): a string to parse
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200891 * @len: length of @str, or -1 if string is `NULL` terminated
Behdad Esfahbod72364102017-01-20 20:16:53 -0800892 * @feature: (out): the #hb_feature_t to initialize with the parsed values
893 *
894 * Parses a string into a #hb_feature_t.
895 *
Khaled Hosny6bd4c082019-02-19 02:23:58 +0200896 * The format for specifying feature strings follows. All valid CSS
David Corbett45adc182019-02-18 22:30:40 -0500897 * font-feature-settings values other than 'normal' and the global values are
898 * also accepted, though not documented below. CSS string escapes are not
899 * supported.
Khaled Hosny6bd4c082019-02-19 02:23:58 +0200900 *
901 * The range indices refer to the positions between Unicode characters. The
902 * position before the first character is always 0.
903 *
904 * The format is Python-esque. Here is how it all works:
905 *
906 * <informaltable pgwide='1' align='left' frame='none'>
907 * <tgroup cols='5'>
908 * <thead>
909 * <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
910 * </thead>
911 * <tbody>
912 * <row><entry>Setting value:</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</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
915 * <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
916 * <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
917 * <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
918 * <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>
919 * <row><entry>Setting index:</entry></row>
920 * <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
921 * <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
922 * <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>
923 * <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>
924 * <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>
925 * <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
926 * <row><entry>Mixing it all:</entry></row>
927 * <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>
928 * </tbody>
929 * </tgroup>
930 * </informaltable>
Behdad Esfahbod72364102017-01-20 20:16:53 -0800931 *
932 * Return value:
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200933 * `true` if @str is successfully parsed, `false` otherwise
Behdad Esfahbod72364102017-01-20 20:16:53 -0800934 *
935 * Since: 0.9.5
936 **/
937hb_bool_t
938hb_feature_from_string (const char *str, int len,
939 hb_feature_t *feature)
940{
941 hb_feature_t feat;
942
943 if (len < 0)
944 len = strlen (str);
945
946 if (likely (parse_one_feature (&str, str + len, &feat)))
947 {
948 if (feature)
949 *feature = feat;
950 return true;
951 }
952
953 if (feature)
Behdad Esfahbodac0efaf2022-11-22 12:50:36 -0700954 hb_memset (feature, 0, sizeof (*feature));
Behdad Esfahbod72364102017-01-20 20:16:53 -0800955 return false;
956}
957
958/**
959 * hb_feature_to_string:
960 * @feature: an #hb_feature_t to convert
961 * @buf: (array length=size) (out): output string
962 * @size: the allocated size of @buf
963 *
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200964 * Converts a #hb_feature_t into a `NULL`-terminated string in the format
Behdad Esfahbod72364102017-01-20 20:16:53 -0800965 * understood by hb_feature_from_string(). The client in responsible for
966 * allocating big enough size for @buf, 128 bytes is more than enough.
967 *
968 * Since: 0.9.5
969 **/
970void
971hb_feature_to_string (hb_feature_t *feature,
972 char *buf, unsigned int size)
973{
974 if (unlikely (!size)) return;
975
976 char s[128];
977 unsigned int len = 0;
978 if (feature->value == 0)
979 s[len++] = '-';
980 hb_tag_to_string (feature->tag, s + len);
981 len += 4;
982 while (len && s[len - 1] == ' ')
983 len--;
Evgeniy Reiznerb79ceac2019-12-15 16:50:01 +0200984 if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800985 {
986 s[len++] = '[';
987 if (feature->start)
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700988 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
Behdad Esfahbod72364102017-01-20 20:16:53 -0800989 if (feature->end != feature->start + 1) {
990 s[len++] = ':';
Evgeniy Reiznerb79ceac2019-12-15 16:50:01 +0200991 if (feature->end != HB_FEATURE_GLOBAL_END)
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700992 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
Behdad Esfahbod72364102017-01-20 20:16:53 -0800993 }
994 s[len++] = ']';
995 }
996 if (feature->value > 1)
997 {
998 s[len++] = '=';
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700999 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
Behdad Esfahbod72364102017-01-20 20:16:53 -08001000 }
1001 assert (len < ARRAY_LENGTH (s));
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001002 len = hb_min (len, size - 1);
Behdad Esfahbod59c45f62022-11-22 12:54:50 -07001003 hb_memcpy (buf, s, len);
Behdad Esfahbod72364102017-01-20 20:16:53 -08001004 buf[len] = '\0';
1005}
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001006
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001007/* hb_variation_t */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001008
1009static bool
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001010parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001011{
1012 parse_char (pp, end, '='); /* Optional. */
Ebrahim Byagowi57f88e12019-09-04 01:20:50 +04301013 double v;
1014 if (unlikely (!hb_parse_double (pp, end, &v))) return false;
1015
1016 variation->value = v;
1017 return true;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001018}
1019
1020static bool
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001021parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001022{
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001023 return parse_tag (pp, end, &variation->tag) &&
1024 parse_variation_value (pp, end, variation) &&
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001025 parse_space (pp, end) &&
1026 *pp == end;
1027}
1028
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001029/**
1030 * hb_variation_from_string:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001031 * @str: (array length=len) (element-type uint8_t): a string to parse
Khaled Hosny98e90cc2022-06-30 08:43:57 +02001032 * @len: length of @str, or -1 if string is `NULL` terminated
Khaled Hosny9b602e82020-12-30 23:28:54 +02001033 * @variation: (out): the #hb_variation_t to initialize with the parsed values
1034 *
1035 * Parses a string into a #hb_variation_t.
1036 *
1037 * The format for specifying variation settings follows. All valid CSS
1038 * font-variation-settings values other than 'normal' and 'inherited' are also
1039 * accepted, though, not documented below.
1040 *
1041 * The format is a tag, optionally followed by an equals sign, followed by a
1042 * number. For example `wght=500`, or `slnt=-7.5`.
1043 *
1044 * Return value:
Khaled Hosny98e90cc2022-06-30 08:43:57 +02001045 * `true` if @str is successfully parsed, `false` otherwise
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001046 *
1047 * Since: 1.4.2
1048 */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001049hb_bool_t
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001050hb_variation_from_string (const char *str, int len,
1051 hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001052{
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001053 hb_variation_t var;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001054
1055 if (len < 0)
1056 len = strlen (str);
1057
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001058 if (likely (parse_one_variation (&str, str + len, &var)))
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001059 {
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001060 if (variation)
1061 *variation = var;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001062 return true;
1063 }
1064
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001065 if (variation)
Behdad Esfahbodac0efaf2022-11-22 12:50:36 -07001066 hb_memset (variation, 0, sizeof (*variation));
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001067 return false;
1068}
1069
Behdad Esfahbod13643932022-01-12 10:54:28 -07001070#ifndef HB_NO_SETLOCALE
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001071
1072static inline void free_static_C_locale ();
1073
Behdad Esfahbod13643932022-01-12 10:54:28 -07001074static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,
Behdad Esfahbod68937232022-02-11 13:16:25 -06001075 hb_C_locale_lazy_loader_t>
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001076{
Behdad Esfahbod13643932022-01-12 10:54:28 -07001077 static hb_locale_t create ()
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001078 {
Behdad Esfahbod13643932022-01-12 10:54:28 -07001079 hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001080 if (!l)
1081 return l;
1082
1083 hb_atexit (free_static_C_locale);
1084
1085 return l;
1086 }
Behdad Esfahbod13643932022-01-12 10:54:28 -07001087 static void destroy (hb_locale_t l)
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001088 {
1089 freelocale (l);
1090 }
Behdad Esfahbod13643932022-01-12 10:54:28 -07001091 static hb_locale_t get_null ()
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001092 {
Behdad Esfahbod13643932022-01-12 10:54:28 -07001093 return (hb_locale_t) 0;
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001094 }
1095} static_C_locale;
1096
1097static inline
1098void free_static_C_locale ()
1099{
1100 static_C_locale.free_instance ();
1101}
1102
Behdad Esfahbod13643932022-01-12 10:54:28 -07001103static hb_locale_t
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001104get_C_locale ()
1105{
1106 return static_C_locale.get_unconst ();
1107}
1108
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001109#endif
1110
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001111/**
1112 * hb_variation_to_string:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001113 * @variation: an #hb_variation_t to convert
Lorenz Wildberg2ee42bb2022-11-01 13:00:18 +01001114 * @buf: (array length=size) (out caller-allocates): output string
Khaled Hosny9b602e82020-12-30 23:28:54 +02001115 * @size: the allocated size of @buf
1116 *
Khaled Hosny98e90cc2022-06-30 08:43:57 +02001117 * Converts an #hb_variation_t into a `NULL`-terminated string in the format
Khaled Hosny9b602e82020-12-30 23:28:54 +02001118 * understood by hb_variation_from_string(). The client in responsible for
1119 * allocating big enough size for @buf, 128 bytes is more than enough.
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001120 *
1121 * Since: 1.4.2
1122 */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001123void
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001124hb_variation_to_string (hb_variation_t *variation,
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001125 char *buf, unsigned int size)
1126{
1127 if (unlikely (!size)) return;
1128
1129 char s[128];
1130 unsigned int len = 0;
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001131 hb_tag_to_string (variation->tag, s + len);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001132 len += 4;
1133 while (len && s[len - 1] == ' ')
1134 len--;
1135 s[len++] = '=';
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001136
Behdad Esfahbod13643932022-01-12 10:54:28 -07001137 hb_locale_t oldlocale HB_UNUSED;
Behdad Esfahbodb97e4f72022-01-15 17:47:51 -07001138 oldlocale = hb_uselocale (get_C_locale ());
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001139 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
Behdad Esfahbodb97e4f72022-01-15 17:47:51 -07001140 (void) hb_uselocale (oldlocale);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001141
1142 assert (len < ARRAY_LENGTH (s));
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001143 len = hb_min (len, size - 1);
Behdad Esfahbod59c45f62022-11-22 12:54:50 -07001144 hb_memcpy (buf, s, len);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001145 buf[len] = '\0';
1146}
Behdad Esfahbode22a48a2018-07-23 13:24:26 -07001147
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001148/**
1149 * hb_color_get_alpha:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001150 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001151 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001152 * Fetches the alpha channel of the given @color.
1153 *
1154 * Return value: Alpha channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001155 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001156 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001157 */
1158uint8_t
1159(hb_color_get_alpha) (hb_color_t color)
1160{
1161 return hb_color_get_alpha (color);
1162}
1163
1164/**
1165 * hb_color_get_red:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001166 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001167 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001168 * Fetches the red channel of the given @color.
1169 *
1170 * Return value: Red channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001171 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001172 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001173 */
1174uint8_t
1175(hb_color_get_red) (hb_color_t color)
1176{
1177 return hb_color_get_red (color);
1178}
1179
1180/**
1181 * hb_color_get_green:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001182 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001183 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001184 * Fetches the green channel of the given @color.
1185 *
1186 * Return value: Green channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001187 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001188 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001189 */
1190uint8_t
1191(hb_color_get_green) (hb_color_t color)
1192{
1193 return hb_color_get_green (color);
1194}
1195
1196/**
1197 * hb_color_get_blue:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001198 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001199 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001200 * Fetches the blue channel of the given @color.
1201 *
1202 * Return value: Blue channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001203 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001204 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001205 */
1206uint8_t
1207(hb_color_get_blue) (hb_color_t color)
1208{
1209 return hb_color_get_blue (color);
1210}
1211
1212
Behdad Esfahbode22a48a2018-07-23 13:24:26 -07001213/* If there is no visibility control, then hb-static.cc will NOT
1214 * define anything. Instead, we get it to define one set in here
1215 * only, so only libharfbuzz.so defines them, not other libs. */
1216#ifdef HB_NO_VISIBILITY
1217#undef HB_NO_VISIBILITY
1218#include "hb-static.cc"
1219#define HB_NO_VISIBILITY 1
1220#endif