blob: cbba1a7942106eae1a08809bae4bd371a42b2ad7 [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 Esfahbod34fb5522011-05-06 00:04:28 -040032#include <locale.h>
33
Ebrahim Byagowi19b8eb02019-06-11 01:33:30 +043034#ifdef HB_NO_SETLOCALE
35#define setlocale(Category, Locale) "C"
36#endif
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -040037
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -070038/**
39 * SECTION:hb-common
Behdad Esfahbodcf5fa572018-10-27 04:50:38 -070040 * @title: hb-common
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -070041 * @short_description: Common data types
42 * @include: hb.h
43 *
44 * Common data types used across HarfBuzz are defined here.
45 **/
46
47
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050048/* hb_options_t */
49
Behdad Esfahbod4bc16ac2018-07-31 21:05:51 -070050hb_atomic_int_t _hb_options;
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050051
52void
Ebrahim Byagowie4120082018-12-17 21:31:01 +033053_hb_options_init ()
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050054{
55 hb_options_union_t u;
56 u.i = 0;
Bruce Mitchener8d1e4792018-10-18 22:18:42 +070057 u.opts.initialized = true;
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050058
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040059 const char *c = getenv ("HB_OPTIONS");
60 if (c)
61 {
62 while (*c)
63 {
64 const char *p = strchr (c, ':');
65 if (!p)
Ebrahim Byagowia0b4ac42019-08-24 17:57:14 +043066 p = c + strlen (c);
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040067
68#define OPTION(name, symbol) \
GaryQianccf14482019-06-24 12:57:13 -070069 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 -040070
71 OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040072
73#undef OPTION
74
75 c = *p ? p + 1 : p;
76 }
77
78 }
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050079
80 /* This is idempotent and threadsafe. */
Behdad Esfahbod4bc16ac2018-07-31 21:05:51 -070081 _hb_options.set_relaxed (u.i);
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050082}
83
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -040084
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -040085/* hb_tag_t */
86
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040087/**
88 * hb_tag_from_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +010089 * @str: (array length=len) (element-type uint8_t): String to convert
90 * @len: Length of @str, or -1 if it is %NULL-terminated
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040091 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +010092 * Converts a string into an #hb_tag_t. Valid tags
93 * are four characters. Shorter input strings will be
94 * padded with spaces. Longer input strings will be
95 * truncated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040096 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +010097 * Return value: The #hb_tag_t corresponding to @str
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040098 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +043099 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400100 **/
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100101hb_tag_t
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400102hb_tag_from_string (const char *str, int len)
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100103{
104 char tag[4];
105 unsigned int i;
106
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400107 if (!str || !len || !*str)
Behdad Esfahbod7ff74012011-04-11 13:27:30 -0400108 return HB_TAG_NONE;
109
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200110 if (len < 0 || len > 4)
111 len = 4;
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400112 for (i = 0; i < (unsigned) len && str[i]; i++)
113 tag[i] = str[i];
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100114 for (; i < 4; i++)
115 tag[i] = ' ';
116
Behdad Esfahbod8eaff982017-10-31 15:30:06 -0600117 return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100118}
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -0400119
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400120/**
121 * hb_tag_to_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100122 * @tag: #hb_tag_t to convert
123 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400124 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100125 * Converts an #hb_tag_t to a string and returns it in @buf.
126 * Strings will be four characters long.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400127 *
Sascha Brawer01c3a882015-06-01 13:22:01 +0200128 * Since: 0.9.5
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400129 **/
Behdad Esfahbode30ebd22012-09-06 22:09:06 -0400130void
131hb_tag_to_string (hb_tag_t tag, char *buf)
132{
133 buf[0] = (char) (uint8_t) (tag >> 24);
134 buf[1] = (char) (uint8_t) (tag >> 16);
135 buf[2] = (char) (uint8_t) (tag >> 8);
136 buf[3] = (char) (uint8_t) (tag >> 0);
137}
138
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -0400139
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400140/* hb_direction_t */
141
142const char direction_strings[][4] = {
143 "ltr",
144 "rtl",
145 "ttb",
146 "btt"
147};
148
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400149/**
150 * hb_direction_from_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100151 * @str: (array length=len) (element-type uint8_t): String to convert
152 * @len: Length of @str, or -1 if it is %NULL-terminated
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400153 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100154 * Converts a string to an #hb_direction_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400155 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100156 * Matching is loose and applies only to the first letter. For
157 * examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430158 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100159 * Unmatched strings will return #HB_DIRECTION_INVALID.
160 *
161 * Return value: The #hb_direction_t matching @str
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400162 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430163 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400164 **/
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400165hb_direction_t
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200166hb_direction_from_string (const char *str, int len)
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400167{
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200168 if (unlikely (!str || !len || !*str))
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400169 return HB_DIRECTION_INVALID;
170
171 /* Lets match loosely: just match the first letter, such that
172 * all of "ltr", "left-to-right", etc work!
173 */
174 char c = TOLOWER (str[0]);
175 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
176 if (c == direction_strings[i][0])
Behdad Esfahbod4bf90f62012-04-12 17:38:23 -0400177 return (hb_direction_t) (HB_DIRECTION_LTR + i);
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400178
179 return HB_DIRECTION_INVALID;
180}
181
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400182/**
183 * hb_direction_to_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100184 * @direction: The #hb_direction_t to convert
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400185 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100186 * Converts an #hb_direction_t to a string.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400187 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100188 * Return value: (transfer none): The string corresponding to @direction
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400189 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430190 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400191 **/
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400192const char *
193hb_direction_to_string (hb_direction_t direction)
194{
Behdad Esfahbod4bf90f62012-04-12 17:38:23 -0400195 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
196 < ARRAY_LENGTH (direction_strings)))
197 return direction_strings[direction - HB_DIRECTION_LTR];
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400198
199 return "invalid";
200}
201
202
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400203/* hb_language_t */
204
Behdad Esfahbod1bc1cb32012-06-16 15:21:55 -0400205struct hb_language_impl_t {
Behdad Esfahbod3cbdf702011-04-15 12:32:06 -0400206 const char s[1];
207};
208
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400209static const char canon_map[256] = {
210 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
211 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
212 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
213 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
David Corbett018ba462018-11-23 13:21:22 -0500214 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400215 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
216 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
217 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
218};
219
Behdad Esfahbodf3b170b2015-04-08 16:26:24 -0700220static bool
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400221lang_equal (hb_language_t v1,
222 const void *v2)
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400223{
Behdad Esfahbodc57d4542011-04-20 18:50:27 -0400224 const unsigned char *p1 = (const unsigned char *) v1;
225 const unsigned char *p2 = (const unsigned char *) v2;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400226
Chris Petersonaacca372017-04-17 23:25:24 -0700227 while (*p1 && *p1 == canon_map[*p2]) {
228 p1++;
229 p2++;
230 }
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400231
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400232 return *p1 == canon_map[*p2];
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400233}
234
235#if 0
236static unsigned int
237lang_hash (const void *key)
238{
239 const unsigned char *p = key;
240 unsigned int h = 0;
241 while (canon_map[*p])
242 {
243 h = (h << 5) - h + canon_map[*p];
244 p++;
245 }
246
247 return h;
248}
249#endif
250
251
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400252struct hb_language_item_t {
253
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400254 struct hb_language_item_t *next;
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400255 hb_language_t lang;
256
Ebrahim Byagowib2ebaa92018-12-16 22:38:10 +0330257 bool operator == (const char *s) const
258 { return lang_equal (lang, s); }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400259
Behdad Esfahbodbb48bf52021-07-08 10:53:45 -0600260 hb_language_item_t & operator = (const char *s)
261 {
262 /* We can't call strdup(), because we allow custom allocators. */
Sebastian Rasmussen92e2c4b2017-05-29 12:53:30 -0500263 size_t len = strlen(s) + 1;
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600264 lang = (hb_language_t) hb_malloc(len);
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400265 if (likely (lang))
266 {
267 memcpy((unsigned char *) lang, s, len);
268 for (unsigned char *p = (unsigned char *) lang; *p; p++)
269 *p = canon_map[*p];
270 }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400271
272 return *this;
273 }
274
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600275 void fini () { hb_free ((void *) lang); }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400276};
277
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400278
Behdad Esfahbodbb48bf52021-07-08 10:53:45 -0600279/* Thread-safe lockfree language list */
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400280
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700281static hb_atomic_ptr_t <hb_language_item_t> langs;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400282
Behdad Esfahbodb89c7fd2018-11-21 12:32:48 -0500283#if HB_USE_ATEXIT
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400284static void
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330285free_langs ()
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400286{
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700287retry:
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500288 hb_language_item_t *first_lang = langs;
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700289 if (unlikely (!langs.cmpexch (first_lang, nullptr)))
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700290 goto retry;
291
292 while (first_lang) {
293 hb_language_item_t *next = first_lang->next;
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400294 first_lang->fini ();
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600295 hb_free (first_lang);
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700296 first_lang = next;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400297 }
298}
jfkthame0082dbe2014-03-16 08:25:17 +0000299#endif
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400300
301static hb_language_item_t *
302lang_find_or_insert (const char *key)
303{
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400304retry:
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500305 hb_language_item_t *first_lang = langs;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400306
307 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
308 if (*lang == key)
309 return lang;
310
311 /* Not found; allocate one. */
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600312 hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400313 if (unlikely (!lang))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200314 return nullptr;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400315 lang->next = first_lang;
316 *lang = key;
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400317 if (unlikely (!lang->lang))
318 {
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600319 hb_free (lang);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200320 return nullptr;
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400321 }
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400322
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700323 if (unlikely (!langs.cmpexch (first_lang, lang)))
324 {
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400325 lang->fini ();
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600326 hb_free (lang);
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400327 goto retry;
328 }
329
Behdad Esfahbodb89c7fd2018-11-21 12:32:48 -0500330#if HB_USE_ATEXIT
Behdad Esfahbod04aed572012-06-05 18:30:19 -0400331 if (!first_lang)
332 atexit (free_langs); /* First person registers atexit() callback. */
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400333#endif
334
335 return lang;
336}
337
Behdad Esfahbodb8d61832011-05-05 15:14:04 -0400338
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400339/**
340 * hb_language_from_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400341 * @str: (array length=len) (element-type uint8_t): a string representing
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200342 * a BCP 47 language tag
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400343 * @len: length of the @str, or -1 if it is %NULL-terminated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400344 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200345 * Converts @str representing a BCP 47 language tag to the corresponding
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400346 * #hb_language_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400347 *
Khaled Hosny04f89e82015-04-10 17:49:01 +0200348 * Return value: (transfer none):
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200349 * The #hb_language_t corresponding to the BCP 47 language tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400350 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430351 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400352 **/
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400353hb_language_t
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200354hb_language_from_string (const char *str, int len)
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400355{
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200356 if (!str || !len || !*str)
Behdad Esfahbod1a64f6e2011-05-13 22:55:32 -0400357 return HB_LANGUAGE_INVALID;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400358
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200359 hb_language_item_t *item = nullptr;
Behdad Esfahbod48360ec2013-09-26 16:48:42 -0400360 if (len >= 0)
361 {
Behdad Esfahboddac86022014-06-03 17:57:00 -0400362 /* NUL-terminate it. */
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100363 char strbuf[64];
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700364 len = hb_min (len, (int) sizeof (strbuf) - 1);
Behdad Esfahboddac86022014-06-03 17:57:00 -0400365 memcpy (strbuf, str, len);
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200366 strbuf[len] = '\0';
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100367 item = lang_find_or_insert (strbuf);
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200368 }
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100369 else
370 item = lang_find_or_insert (str);
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400371
Behdad Esfahbod1a64f6e2011-05-13 22:55:32 -0400372 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400373}
374
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400375/**
376 * hb_language_to_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100377 * @language: The #hb_language_t to convert
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400378 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100379 * Converts an #hb_language_t to a string.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400380 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400381 * Return value: (transfer none):
382 * A %NULL-terminated string representing the @language. Must not be freed by
383 * the caller.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400384 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430385 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400386 **/
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400387const char *
388hb_language_to_string (hb_language_t language)
389{
Ebrahim Byagowie6909ee2019-09-18 22:12:25 +0430390 if (unlikely (!language)) return nullptr;
Ebrahim Byagowid8af9ee2019-09-18 00:47:55 +0430391
Behdad Esfahbod3cbdf702011-04-15 12:32:06 -0400392 return language->s;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400393}
394
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400395/**
396 * hb_language_get_default:
397 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100398 * Fetch the default language from current locale.
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430399 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100400 * <note>Note that the first time this function is called, it calls
Behdad Esfahbodba0f0f12018-09-30 03:49:52 -0400401 * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying
402 * setlocale function is, in many implementations, NOT threadsafe. To avoid
403 * problems, call this function once before multiple threads can call it.
404 * This function is only used from hb_buffer_guess_segment_properties() by
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100405 * HarfBuzz itself.</note>
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400406 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100407 * Return value: (transfer none): The default language of the locale as
408 * an #hb_language_t
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400409 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430410 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400411 **/
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400412hb_language_t
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330413hb_language_get_default ()
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400414{
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700415 static hb_atomic_ptr_t <hb_language_t> default_language;
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400416
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500417 hb_language_t language = default_language;
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700418 if (unlikely (language == HB_LANGUAGE_INVALID))
419 {
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200420 language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700421 (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400422 }
423
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700424 return language;
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400425}
426
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400427
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400428/* hb_script_t */
429
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400430/**
431 * hb_script_from_iso15924_tag:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200432 * @tag: an #hb_tag_t representing an ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400433 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200434 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400435 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430436 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200437 * An #hb_script_t corresponding to the ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400438 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430439 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400440 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400441hb_script_t
442hb_script_from_iso15924_tag (hb_tag_t tag)
443{
Behdad Esfahbodf144a8e2011-04-20 02:54:42 -0400444 if (unlikely (tag == HB_TAG_NONE))
445 return HB_SCRIPT_INVALID;
446
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400447 /* Be lenient, adjust case (one capital letter followed by three small letters) */
Behdad Esfahbod76271002014-07-11 14:54:42 -0400448 tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400449
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400450 switch (tag) {
Behdad Esfahbodd02985e2011-05-02 12:35:14 -0400451
452 /* These graduated from the 'Q' private-area codes, but
453 * the old code is still aliased by Unicode, and the Qaai
454 * one in use by ICU. */
455 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
456 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
457
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +0430458 /* Script variants from https://unicode.org/iso15924/ */
David Corbett3bd43bd2020-11-16 21:55:02 -0500459 case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400460 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
David Corbett3bd43bd2020-11-16 21:55:02 -0500461 case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;
462 case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;
463 case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;
464 case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400465 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
466 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
467 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
468 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
469 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
470 }
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400471
472 /* If it looks right, just use the tag as a script */
Behdad Esfahbod76271002014-07-11 14:54:42 -0400473 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400474 return (hb_script_t) tag;
475
476 /* Otherwise, return unknown */
477 return HB_SCRIPT_UNKNOWN;
478}
479
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400480/**
481 * hb_script_from_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400482 * @str: (array length=len) (element-type uint8_t): a string representing an
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200483 * ISO 15924 tag.
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400484 * @len: length of the @str, or -1 if it is %NULL-terminated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400485 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200486 * Converts a string @str representing an ISO 15924 script tag to a
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400487 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
488 * hb_script_from_iso15924_tag().
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400489 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430490 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200491 * An #hb_script_t corresponding to the ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400492 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430493 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400494 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400495hb_script_t
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400496hb_script_from_string (const char *str, int len)
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400497{
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400498 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400499}
500
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400501/**
502 * hb_script_to_iso15924_tag:
David Corbett5daeff32019-04-17 09:11:44 -0400503 * @script: an #hb_script_t to convert.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400504 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100505 * Converts an #hb_script_t to a corresponding ISO 15924 script tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400506 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400507 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200508 * An #hb_tag_t representing an ISO 15924 script tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400509 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430510 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400511 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400512hb_tag_t
513hb_script_to_iso15924_tag (hb_script_t script)
514{
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400515 return (hb_tag_t) script;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400516}
517
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400518/**
519 * hb_script_get_horizontal_direction:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100520 * @script: The #hb_script_t to query
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400521 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100522 * Fetches the #hb_direction_t of a script when it is
523 * set horizontally. All right-to-left scripts will return
524 * #HB_DIRECTION_RTL. All left-to-right scripts will return
Khaled Hosny8586f152020-12-24 22:23:47 +0200525 * #HB_DIRECTION_LTR. Scripts that can be written either
526 * horizontally or vertically will return #HB_DIRECTION_INVALID.
527 * Unknown scripts will return #HB_DIRECTION_LTR.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400528 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100529 * Return value: The horizontal #hb_direction_t of @script
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400530 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430531 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400532 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400533hb_direction_t
534hb_script_get_horizontal_direction (hb_script_t script)
535{
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +0430536 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400537 switch ((hb_tag_t) script)
538 {
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500539 /* Unicode-1.1 additions */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400540 case HB_SCRIPT_ARABIC:
541 case HB_SCRIPT_HEBREW:
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500542
543 /* Unicode-3.0 additions */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400544 case HB_SCRIPT_SYRIAC:
545 case HB_SCRIPT_THAANA:
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400546
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400547 /* Unicode-4.0 additions */
548 case HB_SCRIPT_CYPRIOT:
549
Behdad Esfahbod50e810c2012-03-07 12:49:08 -0500550 /* Unicode-4.1 additions */
551 case HB_SCRIPT_KHAROSHTHI:
552
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400553 /* Unicode-5.0 additions */
554 case HB_SCRIPT_PHOENICIAN:
555 case HB_SCRIPT_NKO:
556
Behdad Esfahbod50e810c2012-03-07 12:49:08 -0500557 /* Unicode-5.1 additions */
558 case HB_SCRIPT_LYDIAN:
559
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400560 /* Unicode-5.2 additions */
561 case HB_SCRIPT_AVESTAN:
562 case HB_SCRIPT_IMPERIAL_ARAMAIC:
563 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
564 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
565 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
566 case HB_SCRIPT_OLD_TURKIC:
567 case HB_SCRIPT_SAMARITAN:
568
569 /* Unicode-6.0 additions */
570 case HB_SCRIPT_MANDAIC:
571
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500572 /* Unicode-6.1 additions */
573 case HB_SCRIPT_MEROITIC_CURSIVE:
574 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
575
Behdad Esfahboda4a78992014-04-28 15:06:42 -0700576 /* Unicode-7.0 additions */
577 case HB_SCRIPT_MANICHAEAN:
578 case HB_SCRIPT_MENDE_KIKAKUI:
579 case HB_SCRIPT_NABATAEAN:
580 case HB_SCRIPT_OLD_NORTH_ARABIAN:
581 case HB_SCRIPT_PALMYRENE:
582 case HB_SCRIPT_PSALTER_PAHLAVI:
583
Behdad Esfahbod64a27262015-07-15 01:36:39 +0100584 /* Unicode-8.0 additions */
Cosimo Lupoc8f2a4f2018-01-18 22:49:40 +0100585 case HB_SCRIPT_HATRAN:
Behdad Esfahbod64a27262015-07-15 01:36:39 +0100586
Behdad Esfahbod691086f2016-05-06 12:08:18 +0100587 /* Unicode-9.0 additions */
588 case HB_SCRIPT_ADLAM:
589
Behdad Esfahbod060e6b42018-06-05 17:31:46 -0700590 /* Unicode-11.0 additions */
591 case HB_SCRIPT_HANIFI_ROHINGYA:
592 case HB_SCRIPT_OLD_SOGDIAN:
593 case HB_SCRIPT_SOGDIAN:
594
David Corbett665483c2020-04-01 17:28:12 -0400595 /* Unicode-12.0 additions */
596 case HB_SCRIPT_ELYMAIC:
597
David Corbettfd748fa2020-03-15 15:59:31 -0400598 /* Unicode-13.0 additions */
599 case HB_SCRIPT_CHORASMIAN:
600 case HB_SCRIPT_YEZIDI:
601
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400602 return HB_DIRECTION_RTL;
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700603
604
605 /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
David Corbettd8d1e7d2018-09-17 11:09:51 -0400606 case HB_SCRIPT_OLD_HUNGARIAN:
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700607 case HB_SCRIPT_OLD_ITALIC:
David Corbett46d8f0d2018-07-06 15:47:03 -0400608 case HB_SCRIPT_RUNIC:
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700609
610 return HB_DIRECTION_INVALID;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400611 }
612
613 return HB_DIRECTION_LTR;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400614}
615
616
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400617/* hb_version */
618
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -0700619
620/**
621 * SECTION:hb-version
Behdad Esfahbodcf5fa572018-10-27 04:50:38 -0700622 * @title: hb-version
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -0700623 * @short_description: Information about the version of HarfBuzz in use
624 * @include: hb.h
625 *
626 * These functions and macros allow accessing version of the HarfBuzz
627 * library used at compile- as well as run-time, and to direct code
628 * conditionally based on those versions, again, at compile- or run-time.
629 **/
630
631
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400632/**
633 * hb_version:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100634 * @major: (out): Library major version component
635 * @minor: (out): Library minor version component
636 * @micro: (out): Library micro version component
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400637 *
638 * Returns library version as three integer components.
639 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430640 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400641 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400642void
643hb_version (unsigned int *major,
644 unsigned int *minor,
645 unsigned int *micro)
646{
647 *major = HB_VERSION_MAJOR;
648 *minor = HB_VERSION_MINOR;
649 *micro = HB_VERSION_MICRO;
650}
651
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400652/**
653 * hb_version_string:
654 *
655 * Returns library version as a string with three components.
656 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100657 * Return value: Library version string
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400658 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430659 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400660 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400661const char *
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330662hb_version_string ()
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400663{
664 return HB_VERSION_STRING;
665}
666
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400667/**
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400668 * hb_version_atleast:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100669 * @major: Library major version component
670 * @minor: Library minor version component
671 * @micro: Library micro version component
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400672 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100673 * Tests the library version against a minimum value,
674 * as three integer components.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400675 *
Khaled Hosnya8e72ee2020-12-30 23:08:40 +0200676 * Return value: %true if the library is equal to or greater than
677 * the test value, %false otherwise
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400678 *
Sascha Brawer01c3a882015-06-01 13:22:01 +0200679 * Since: 0.9.30
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400680 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400681hb_bool_t
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400682hb_version_atleast (unsigned int major,
683 unsigned int minor,
684 unsigned int micro)
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400685{
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400686 return HB_VERSION_ATLEAST (major, minor, micro);
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400687}
Behdad Esfahbod72364102017-01-20 20:16:53 -0800688
689
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800690
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800691/* hb_feature_t and hb_variation_t */
Behdad Esfahbod72364102017-01-20 20:16:53 -0800692
693static bool
694parse_space (const char **pp, const char *end)
695{
696 while (*pp < end && ISSPACE (**pp))
697 (*pp)++;
698 return true;
699}
700
701static bool
702parse_char (const char **pp, const char *end, char c)
703{
704 parse_space (pp, end);
705
706 if (*pp == end || **pp != c)
707 return false;
708
709 (*pp)++;
710 return true;
711}
712
713static bool
714parse_uint (const char **pp, const char *end, unsigned int *pv)
715{
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430716 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
717 * such that -1 turns into "big number"... */
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430718 int v;
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430719 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800720
721 *pv = v;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800722 return true;
723}
724
725static bool
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430726parse_uint32 (const char **pp, const char *end, uint32_t *pv)
727{
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430728 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
729 * such that -1 turns into "big number"... */
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430730 int v;
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430731 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430732
733 *pv = v;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800734 return true;
735}
736
737static bool
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430738parse_bool (const char **pp, const char *end, uint32_t *pv)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800739{
740 parse_space (pp, end);
741
742 const char *p = *pp;
743 while (*pp < end && ISALPHA(**pp))
744 (*pp)++;
745
746 /* CSS allows on/off as aliases 1/0. */
David Corbett45adc182019-02-18 22:30:40 -0500747 if (*pp - p == 2
748 && TOLOWER (p[0]) == 'o'
749 && TOLOWER (p[1]) == 'n')
Behdad Esfahbod72364102017-01-20 20:16:53 -0800750 *pv = 1;
David Corbett45adc182019-02-18 22:30:40 -0500751 else if (*pp - p == 3
752 && TOLOWER (p[0]) == 'o'
753 && TOLOWER (p[1]) == 'f'
754 && TOLOWER (p[2]) == 'f')
Behdad Esfahbod72364102017-01-20 20:16:53 -0800755 *pv = 0;
756 else
757 return false;
758
759 return true;
760}
761
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800762/* hb_feature_t */
763
Behdad Esfahbod72364102017-01-20 20:16:53 -0800764static bool
765parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
766{
767 if (parse_char (pp, end, '-'))
768 feature->value = 0;
769 else {
770 parse_char (pp, end, '+');
771 feature->value = 1;
772 }
773
774 return true;
775}
776
777static bool
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800778parse_tag (const char **pp, const char *end, hb_tag_t *tag)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800779{
780 parse_space (pp, end);
781
782 char quote = 0;
783
784 if (*pp < end && (**pp == '\'' || **pp == '"'))
785 {
786 quote = **pp;
787 (*pp)++;
788 }
789
790 const char *p = *pp;
Martin Hosken39607dc2018-08-09 15:16:32 +0700791 while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
Behdad Esfahbod72364102017-01-20 20:16:53 -0800792 (*pp)++;
793
794 if (p == *pp || *pp - p > 4)
795 return false;
796
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800797 *tag = hb_tag_from_string (p, *pp - p);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800798
799 if (quote)
800 {
801 /* CSS expects exactly four bytes. And we only allow quotations for
802 * CSS compatibility. So, enforce the length. */
803 if (*pp - p != 4)
804 return false;
805 if (*pp == end || **pp != quote)
806 return false;
807 (*pp)++;
808 }
809
810 return true;
811}
812
813static bool
814parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
815{
816 parse_space (pp, end);
817
818 bool has_start;
819
Behdad Esfahbodbecd84a2018-09-11 01:26:18 +0200820 feature->start = HB_FEATURE_GLOBAL_START;
821 feature->end = HB_FEATURE_GLOBAL_END;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800822
823 if (!parse_char (pp, end, '['))
824 return true;
825
826 has_start = parse_uint (pp, end, &feature->start);
827
Behdad Esfahbod88694362018-10-23 03:07:48 -0700828 if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
Behdad Esfahbod72364102017-01-20 20:16:53 -0800829 parse_uint (pp, end, &feature->end);
830 } else {
831 if (has_start)
832 feature->end = feature->start + 1;
833 }
834
835 return parse_char (pp, end, ']');
836}
837
838static bool
839parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
840{
841 bool had_equal = parse_char (pp, end, '=');
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430842 bool had_value = parse_uint32 (pp, end, &feature->value) ||
Ebrahim Byagowia0b4ac42019-08-24 17:57:14 +0430843 parse_bool (pp, end, &feature->value);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800844 /* CSS doesn't use equal-sign between tag and value.
845 * If there was an equal-sign, then there *must* be a value.
Bruce Mitchener90218fa2018-01-31 20:44:45 +0700846 * A value without an equal-sign is ok, but not required. */
Behdad Esfahbod72364102017-01-20 20:16:53 -0800847 return !had_equal || had_value;
848}
849
Behdad Esfahbod72364102017-01-20 20:16:53 -0800850static bool
851parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
852{
853 return parse_feature_value_prefix (pp, end, feature) &&
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800854 parse_tag (pp, end, &feature->tag) &&
Behdad Esfahbod72364102017-01-20 20:16:53 -0800855 parse_feature_indices (pp, end, feature) &&
856 parse_feature_value_postfix (pp, end, feature) &&
857 parse_space (pp, end) &&
858 *pp == end;
859}
860
861/**
862 * hb_feature_from_string:
863 * @str: (array length=len) (element-type uint8_t): a string to parse
864 * @len: length of @str, or -1 if string is %NULL terminated
865 * @feature: (out): the #hb_feature_t to initialize with the parsed values
866 *
867 * Parses a string into a #hb_feature_t.
868 *
Khaled Hosny6bd4c082019-02-19 02:23:58 +0200869 * The format for specifying feature strings follows. All valid CSS
David Corbett45adc182019-02-18 22:30:40 -0500870 * font-feature-settings values other than 'normal' and the global values are
871 * also accepted, though not documented below. CSS string escapes are not
872 * supported.
Khaled Hosny6bd4c082019-02-19 02:23:58 +0200873 *
874 * The range indices refer to the positions between Unicode characters. The
875 * position before the first character is always 0.
876 *
877 * The format is Python-esque. Here is how it all works:
878 *
879 * <informaltable pgwide='1' align='left' frame='none'>
880 * <tgroup cols='5'>
881 * <thead>
882 * <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
883 * </thead>
884 * <tbody>
885 * <row><entry>Setting value:</entry></row>
886 * <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
887 * <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
888 * <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
889 * <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
890 * <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
891 * <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>
892 * <row><entry>Setting index:</entry></row>
893 * <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
894 * <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
895 * <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>
896 * <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>
897 * <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>
898 * <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
899 * <row><entry>Mixing it all:</entry></row>
900 * <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>
901 * </tbody>
902 * </tgroup>
903 * </informaltable>
Behdad Esfahbod72364102017-01-20 20:16:53 -0800904 *
905 * Return value:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100906 * %true if @str is successfully parsed, %false otherwise
Behdad Esfahbod72364102017-01-20 20:16:53 -0800907 *
908 * Since: 0.9.5
909 **/
910hb_bool_t
911hb_feature_from_string (const char *str, int len,
912 hb_feature_t *feature)
913{
914 hb_feature_t feat;
915
916 if (len < 0)
917 len = strlen (str);
918
919 if (likely (parse_one_feature (&str, str + len, &feat)))
920 {
921 if (feature)
922 *feature = feat;
923 return true;
924 }
925
926 if (feature)
927 memset (feature, 0, sizeof (*feature));
928 return false;
929}
930
931/**
932 * hb_feature_to_string:
933 * @feature: an #hb_feature_t to convert
934 * @buf: (array length=size) (out): output string
935 * @size: the allocated size of @buf
936 *
937 * Converts a #hb_feature_t into a %NULL-terminated string in the format
938 * understood by hb_feature_from_string(). The client in responsible for
939 * allocating big enough size for @buf, 128 bytes is more than enough.
940 *
941 * Since: 0.9.5
942 **/
943void
944hb_feature_to_string (hb_feature_t *feature,
945 char *buf, unsigned int size)
946{
947 if (unlikely (!size)) return;
948
949 char s[128];
950 unsigned int len = 0;
951 if (feature->value == 0)
952 s[len++] = '-';
953 hb_tag_to_string (feature->tag, s + len);
954 len += 4;
955 while (len && s[len - 1] == ' ')
956 len--;
Evgeniy Reiznerb79ceac2019-12-15 16:50:01 +0200957 if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800958 {
959 s[len++] = '[';
960 if (feature->start)
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700961 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
Behdad Esfahbod72364102017-01-20 20:16:53 -0800962 if (feature->end != feature->start + 1) {
963 s[len++] = ':';
Evgeniy Reiznerb79ceac2019-12-15 16:50:01 +0200964 if (feature->end != HB_FEATURE_GLOBAL_END)
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700965 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
Behdad Esfahbod72364102017-01-20 20:16:53 -0800966 }
967 s[len++] = ']';
968 }
969 if (feature->value > 1)
970 {
971 s[len++] = '=';
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700972 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
Behdad Esfahbod72364102017-01-20 20:16:53 -0800973 }
974 assert (len < ARRAY_LENGTH (s));
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700975 len = hb_min (len, size - 1);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800976 memcpy (buf, s, len);
977 buf[len] = '\0';
978}
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800979
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800980/* hb_variation_t */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800981
982static bool
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800983parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800984{
985 parse_char (pp, end, '='); /* Optional. */
Ebrahim Byagowi57f88e12019-09-04 01:20:50 +0430986 double v;
987 if (unlikely (!hb_parse_double (pp, end, &v))) return false;
988
989 variation->value = v;
990 return true;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800991}
992
993static bool
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800994parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800995{
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800996 return parse_tag (pp, end, &variation->tag) &&
997 parse_variation_value (pp, end, variation) &&
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800998 parse_space (pp, end) &&
999 *pp == end;
1000}
1001
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001002/**
1003 * hb_variation_from_string:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001004 * @str: (array length=len) (element-type uint8_t): a string to parse
1005 * @len: length of @str, or -1 if string is %NULL terminated
1006 * @variation: (out): the #hb_variation_t to initialize with the parsed values
1007 *
1008 * Parses a string into a #hb_variation_t.
1009 *
1010 * The format for specifying variation settings follows. All valid CSS
1011 * font-variation-settings values other than 'normal' and 'inherited' are also
1012 * accepted, though, not documented below.
1013 *
1014 * The format is a tag, optionally followed by an equals sign, followed by a
1015 * number. For example `wght=500`, or `slnt=-7.5`.
1016 *
1017 * Return value:
1018 * %true if @str is successfully parsed, %false otherwise
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001019 *
1020 * Since: 1.4.2
1021 */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001022hb_bool_t
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001023hb_variation_from_string (const char *str, int len,
1024 hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001025{
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001026 hb_variation_t var;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001027
1028 if (len < 0)
1029 len = strlen (str);
1030
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001031 if (likely (parse_one_variation (&str, str + len, &var)))
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001032 {
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001033 if (variation)
1034 *variation = var;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001035 return true;
1036 }
1037
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001038 if (variation)
1039 memset (variation, 0, sizeof (*variation));
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001040 return false;
1041}
1042
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001043/**
1044 * hb_variation_to_string:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001045 * @variation: an #hb_variation_t to convert
1046 * @buf: (array length=size) (out): output string
1047 * @size: the allocated size of @buf
1048 *
1049 * Converts an #hb_variation_t into a %NULL-terminated string in the format
1050 * understood by hb_variation_from_string(). The client in responsible for
1051 * allocating big enough size for @buf, 128 bytes is more than enough.
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001052 *
1053 * Since: 1.4.2
1054 */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001055void
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001056hb_variation_to_string (hb_variation_t *variation,
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001057 char *buf, unsigned int size)
1058{
1059 if (unlikely (!size)) return;
1060
1061 char s[128];
1062 unsigned int len = 0;
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001063 hb_tag_to_string (variation->tag, s + len);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001064 len += 4;
1065 while (len && s[len - 1] == ' ')
1066 len--;
1067 s[len++] = '=';
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001068 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001069
1070 assert (len < ARRAY_LENGTH (s));
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001071 len = hb_min (len, size - 1);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001072 memcpy (buf, s, len);
1073 buf[len] = '\0';
1074}
Behdad Esfahbode22a48a2018-07-23 13:24:26 -07001075
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001076/**
1077 * hb_color_get_alpha:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001078 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001079 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001080 * Fetches the alpha channel of the given @color.
1081 *
1082 * Return value: Alpha channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001083 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001084 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001085 */
1086uint8_t
1087(hb_color_get_alpha) (hb_color_t color)
1088{
1089 return hb_color_get_alpha (color);
1090}
1091
1092/**
1093 * hb_color_get_red:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001094 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001095 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001096 * Fetches the red channel of the given @color.
1097 *
1098 * Return value: Red channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001099 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001100 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001101 */
1102uint8_t
1103(hb_color_get_red) (hb_color_t color)
1104{
1105 return hb_color_get_red (color);
1106}
1107
1108/**
1109 * hb_color_get_green:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001110 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001111 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001112 * Fetches the green channel of the given @color.
1113 *
1114 * Return value: Green channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001115 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001116 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001117 */
1118uint8_t
1119(hb_color_get_green) (hb_color_t color)
1120{
1121 return hb_color_get_green (color);
1122}
1123
1124/**
1125 * hb_color_get_blue:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001126 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001127 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001128 * Fetches the blue channel of the given @color.
1129 *
1130 * Return value: Blue channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001131 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001132 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001133 */
1134uint8_t
1135(hb_color_get_blue) (hb_color_t color)
1136{
1137 return hb_color_get_blue (color);
1138}
1139
1140
Behdad Esfahbode22a48a2018-07-23 13:24:26 -07001141/* If there is no visibility control, then hb-static.cc will NOT
1142 * define anything. Instead, we get it to define one set in here
1143 * only, so only libharfbuzz.so defines them, not other libs. */
1144#ifdef HB_NO_VISIBILITY
1145#undef HB_NO_VISIBILITY
1146#include "hb-static.cc"
1147#define HB_NO_VISIBILITY 1
1148#endif