blob: 4940e7fb561b756b65b9e7c6841f3c1c08b0fac0 [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 Esfahbod83f34672010-05-21 13:43:49 +010030
Behdad Esfahbodc77ae402018-08-25 22:36:36 -070031#include "hb-machinery.hh"
Behdad Esfahbodb8d61832011-05-05 15:14:04 -040032
Behdad Esfahbod34fb5522011-05-06 00:04:28 -040033#include <locale.h>
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -040034#ifdef HAVE_XLOCALE_H
35#include <xlocale.h>
36#endif
Behdad Esfahbod34fb5522011-05-06 00:04:28 -040037
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -040038
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050039/* hb_options_t */
40
Behdad Esfahbod4bc16ac2018-07-31 21:05:51 -070041hb_atomic_int_t _hb_options;
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050042
43void
44_hb_options_init (void)
45{
46 hb_options_union_t u;
47 u.i = 0;
48 u.opts.initialized = 1;
49
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040050 const char *c = getenv ("HB_OPTIONS");
51 if (c)
52 {
53 while (*c)
54 {
55 const char *p = strchr (c, ':');
56 if (!p)
57 p = c + strlen (c);
58
59#define OPTION(name, symbol) \
Behdad Esfahbod7e6e5bf2018-10-10 18:59:07 -040060 if (0 == strncmp (c, name, p - c) && strlen (name) == p - c) u.opts.symbol = true;
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040061
62 OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
63 OPTION ("aat", aat);
64
65#undef OPTION
66
67 c = *p ? p + 1 : p;
68 }
69
70 }
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050071
72 /* This is idempotent and threadsafe. */
Behdad Esfahbod4bc16ac2018-07-31 21:05:51 -070073 _hb_options.set_relaxed (u.i);
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050074}
75
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -040076
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -040077/* hb_tag_t */
78
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040079/**
80 * hb_tag_from_string:
Ebrahim Byagowi70d36542018-03-30 05:00:28 +043081 * @str: (array length=len) (element-type uint8_t):
82 * @len:
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040083 *
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040084 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +043085 *
86 * Return value:
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040087 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +043088 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040089 **/
Behdad Esfahbod83f34672010-05-21 13:43:49 +010090hb_tag_t
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040091hb_tag_from_string (const char *str, int len)
Behdad Esfahbod83f34672010-05-21 13:43:49 +010092{
93 char tag[4];
94 unsigned int i;
95
Behdad Esfahbod70303cf2013-09-06 17:35:57 -040096 if (!str || !len || !*str)
Behdad Esfahbod7ff74012011-04-11 13:27:30 -040097 return HB_TAG_NONE;
98
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +020099 if (len < 0 || len > 4)
100 len = 4;
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400101 for (i = 0; i < (unsigned) len && str[i]; i++)
102 tag[i] = str[i];
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100103 for (; i < 4; i++)
104 tag[i] = ' ';
105
Behdad Esfahbod8eaff982017-10-31 15:30:06 -0600106 return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100107}
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -0400108
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400109/**
110 * hb_tag_to_string:
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430111 * @tag:
112 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t):
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400113 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430114 *
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400115 *
Sascha Brawer01c3a882015-06-01 13:22:01 +0200116 * Since: 0.9.5
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400117 **/
Behdad Esfahbode30ebd22012-09-06 22:09:06 -0400118void
119hb_tag_to_string (hb_tag_t tag, char *buf)
120{
121 buf[0] = (char) (uint8_t) (tag >> 24);
122 buf[1] = (char) (uint8_t) (tag >> 16);
123 buf[2] = (char) (uint8_t) (tag >> 8);
124 buf[3] = (char) (uint8_t) (tag >> 0);
125}
126
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -0400127
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400128/* hb_direction_t */
129
130const char direction_strings[][4] = {
131 "ltr",
132 "rtl",
133 "ttb",
134 "btt"
135};
136
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400137/**
138 * hb_direction_from_string:
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430139 * @str: (array length=len) (element-type uint8_t):
140 * @len:
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400141 *
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400142 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430143 *
144 * Return value:
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400145 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430146 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400147 **/
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400148hb_direction_t
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200149hb_direction_from_string (const char *str, int len)
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400150{
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200151 if (unlikely (!str || !len || !*str))
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400152 return HB_DIRECTION_INVALID;
153
154 /* Lets match loosely: just match the first letter, such that
155 * all of "ltr", "left-to-right", etc work!
156 */
157 char c = TOLOWER (str[0]);
158 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
159 if (c == direction_strings[i][0])
Behdad Esfahbod4bf90f62012-04-12 17:38:23 -0400160 return (hb_direction_t) (HB_DIRECTION_LTR + i);
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400161
162 return HB_DIRECTION_INVALID;
163}
164
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400165/**
166 * hb_direction_to_string:
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430167 * @direction:
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400168 *
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400169 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430170 *
171 * Return value: (transfer none):
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400172 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430173 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400174 **/
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400175const char *
176hb_direction_to_string (hb_direction_t direction)
177{
Behdad Esfahbod4bf90f62012-04-12 17:38:23 -0400178 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
179 < ARRAY_LENGTH (direction_strings)))
180 return direction_strings[direction - HB_DIRECTION_LTR];
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400181
182 return "invalid";
183}
184
185
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400186/* hb_language_t */
187
Behdad Esfahbod1bc1cb32012-06-16 15:21:55 -0400188struct hb_language_impl_t {
Behdad Esfahbod3cbdf702011-04-15 12:32:06 -0400189 const char s[1];
190};
191
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400192static const char canon_map[256] = {
193 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
194 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
195 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
196 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
197 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
198 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
199 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
200 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
201};
202
Behdad Esfahbodf3b170b2015-04-08 16:26:24 -0700203static bool
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400204lang_equal (hb_language_t v1,
205 const void *v2)
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400206{
Behdad Esfahbodc57d4542011-04-20 18:50:27 -0400207 const unsigned char *p1 = (const unsigned char *) v1;
208 const unsigned char *p2 = (const unsigned char *) v2;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400209
Chris Petersonaacca372017-04-17 23:25:24 -0700210 while (*p1 && *p1 == canon_map[*p2]) {
211 p1++;
212 p2++;
213 }
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400214
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400215 return *p1 == canon_map[*p2];
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400216}
217
218#if 0
219static unsigned int
220lang_hash (const void *key)
221{
222 const unsigned char *p = key;
223 unsigned int h = 0;
224 while (canon_map[*p])
225 {
226 h = (h << 5) - h + canon_map[*p];
227 p++;
228 }
229
230 return h;
231}
232#endif
233
234
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400235struct hb_language_item_t {
236
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400237 struct hb_language_item_t *next;
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400238 hb_language_t lang;
239
240 inline bool operator == (const char *s) const {
241 return lang_equal (lang, s);
242 }
243
244 inline hb_language_item_t & operator = (const char *s) {
Sebastian Rasmussen92e2c4b2017-05-29 12:53:30 -0500245 /* If a custom allocated is used calling strdup() pairs
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400246 badly with a call to the custom free() in fini() below.
Sebastian Rasmussen92e2c4b2017-05-29 12:53:30 -0500247 Therefore don't call strdup(), implement its behavior.
248 */
249 size_t len = strlen(s) + 1;
250 lang = (hb_language_t) malloc(len);
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400251 if (likely (lang))
252 {
253 memcpy((unsigned char *) lang, s, len);
254 for (unsigned char *p = (unsigned char *) lang; *p; p++)
255 *p = canon_map[*p];
256 }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400257
258 return *this;
259 }
260
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400261 void fini (void) { free ((void *) lang); }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400262};
263
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400264
265/* Thread-safe lock-free language list */
266
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700267static hb_atomic_ptr_t <hb_language_item_t> langs;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400268
Behdad Esfahbod38fb30d2014-08-06 13:34:49 -0400269#ifdef HB_USE_ATEXIT
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400270static void
271free_langs (void)
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400272{
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700273retry:
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700274 hb_language_item_t *first_lang = langs.get ();
275 if (unlikely (!langs.cmpexch (first_lang, nullptr)))
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700276 goto retry;
277
278 while (first_lang) {
279 hb_language_item_t *next = first_lang->next;
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400280 first_lang->fini ();
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700281 free (first_lang);
282 first_lang = next;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400283 }
284}
jfkthame0082dbe2014-03-16 08:25:17 +0000285#endif
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400286
287static hb_language_item_t *
288lang_find_or_insert (const char *key)
289{
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400290retry:
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700291 hb_language_item_t *first_lang = langs.get ();
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400292
293 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
294 if (*lang == key)
295 return lang;
296
297 /* Not found; allocate one. */
298 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
299 if (unlikely (!lang))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200300 return nullptr;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400301 lang->next = first_lang;
302 *lang = key;
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400303 if (unlikely (!lang->lang))
304 {
305 free (lang);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200306 return nullptr;
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400307 }
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400308
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700309 if (unlikely (!langs.cmpexch (first_lang, lang)))
310 {
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400311 lang->fini ();
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400312 free (lang);
313 goto retry;
314 }
315
Behdad Esfahbod38fb30d2014-08-06 13:34:49 -0400316#ifdef HB_USE_ATEXIT
Behdad Esfahbod04aed572012-06-05 18:30:19 -0400317 if (!first_lang)
318 atexit (free_langs); /* First person registers atexit() callback. */
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400319#endif
320
321 return lang;
322}
323
Behdad Esfahbodb8d61832011-05-05 15:14:04 -0400324
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400325/**
326 * hb_language_from_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400327 * @str: (array length=len) (element-type uint8_t): a string representing
David Corbetta03f5f42017-12-28 22:59:29 +0800328 * a BCP 47 language tag
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400329 * @len: length of the @str, or -1 if it is %NULL-terminated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400330 *
David Corbetta03f5f42017-12-28 22:59:29 +0800331 * Converts @str representing a BCP 47 language tag to the corresponding
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400332 * #hb_language_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400333 *
Khaled Hosny04f89e82015-04-10 17:49:01 +0200334 * Return value: (transfer none):
David Corbetta03f5f42017-12-28 22:59:29 +0800335 * The #hb_language_t corresponding to the BCP 47 language tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400336 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430337 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400338 **/
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400339hb_language_t
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200340hb_language_from_string (const char *str, int len)
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400341{
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200342 if (!str || !len || !*str)
Behdad Esfahbod1a64f6e2011-05-13 22:55:32 -0400343 return HB_LANGUAGE_INVALID;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400344
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200345 hb_language_item_t *item = nullptr;
Behdad Esfahbod48360ec2013-09-26 16:48:42 -0400346 if (len >= 0)
347 {
Behdad Esfahboddac86022014-06-03 17:57:00 -0400348 /* NUL-terminate it. */
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100349 char strbuf[64];
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200350 len = MIN (len, (int) sizeof (strbuf) - 1);
Behdad Esfahboddac86022014-06-03 17:57:00 -0400351 memcpy (strbuf, str, len);
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200352 strbuf[len] = '\0';
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100353 item = lang_find_or_insert (strbuf);
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200354 }
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100355 else
356 item = lang_find_or_insert (str);
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400357
Behdad Esfahbod1a64f6e2011-05-13 22:55:32 -0400358 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400359}
360
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400361/**
362 * hb_language_to_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400363 * @language: an #hb_language_t to convert.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400364 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400365 * See hb_language_from_string().
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400366 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400367 * Return value: (transfer none):
368 * A %NULL-terminated string representing the @language. Must not be freed by
369 * the caller.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400370 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430371 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400372 **/
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400373const char *
374hb_language_to_string (hb_language_t language)
375{
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200376 /* This is actually nullptr-safe! */
Behdad Esfahbod3cbdf702011-04-15 12:32:06 -0400377 return language->s;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400378}
379
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400380/**
381 * hb_language_get_default:
382 *
Behdad Esfahbodba0f0f12018-09-30 03:49:52 -0400383 * Get default language from current locale.
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430384 *
Behdad Esfahbodba0f0f12018-09-30 03:49:52 -0400385 * Note that the first time this function is called, it calls
386 * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying
387 * setlocale function is, in many implementations, NOT threadsafe. To avoid
388 * problems, call this function once before multiple threads can call it.
389 * This function is only used from hb_buffer_guess_segment_properties() by
390 * HarfBuzz itself.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400391 *
Behdad Esfahbod351f68f2015-06-12 17:46:06 -0700392 * Return value: (transfer none):
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400393 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430394 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400395 **/
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400396hb_language_t
397hb_language_get_default (void)
398{
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700399 static hb_atomic_ptr_t <hb_language_t> default_language;
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400400
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700401 hb_language_t language = default_language.get ();
402 if (unlikely (language == HB_LANGUAGE_INVALID))
403 {
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200404 language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700405 (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400406 }
407
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700408 return language;
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400409}
410
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400411
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400412/* hb_script_t */
413
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400414/**
415 * hb_script_from_iso15924_tag:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400416 * @tag: an #hb_tag_t representing an ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400417 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400418 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400419 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430420 * Return value:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400421 * An #hb_script_t corresponding to the ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400422 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430423 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400424 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400425hb_script_t
426hb_script_from_iso15924_tag (hb_tag_t tag)
427{
Behdad Esfahbodf144a8e2011-04-20 02:54:42 -0400428 if (unlikely (tag == HB_TAG_NONE))
429 return HB_SCRIPT_INVALID;
430
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400431 /* Be lenient, adjust case (one capital letter followed by three small letters) */
Behdad Esfahbod76271002014-07-11 14:54:42 -0400432 tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400433
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400434 switch (tag) {
Behdad Esfahbodd02985e2011-05-02 12:35:14 -0400435
436 /* These graduated from the 'Q' private-area codes, but
437 * the old code is still aliased by Unicode, and the Qaai
438 * one in use by ICU. */
439 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
440 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
441
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +0430442 /* Script variants from https://unicode.org/iso15924/ */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400443 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400444 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
445 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
446 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
447 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
448 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
449 }
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400450
451 /* If it looks right, just use the tag as a script */
Behdad Esfahbod76271002014-07-11 14:54:42 -0400452 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400453 return (hb_script_t) tag;
454
455 /* Otherwise, return unknown */
456 return HB_SCRIPT_UNKNOWN;
457}
458
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400459/**
460 * hb_script_from_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400461 * @str: (array length=len) (element-type uint8_t): a string representing an
462 * ISO 15924 tag.
463 * @len: length of the @str, or -1 if it is %NULL-terminated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400464 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400465 * Converts a string @str representing an ISO 15924 script tag to a
466 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
467 * hb_script_from_iso15924_tag().
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400468 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430469 * Return value:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400470 * An #hb_script_t corresponding to the ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400471 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430472 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400473 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400474hb_script_t
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400475hb_script_from_string (const char *str, int len)
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400476{
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400477 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400478}
479
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400480/**
481 * hb_script_to_iso15924_tag:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400482 * @script: an #hb_script_ to convert.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400483 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400484 * See hb_script_from_iso15924_tag().
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400485 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400486 * Return value:
487 * An #hb_tag_t representing an ISO 15924 script tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400488 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430489 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400490 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400491hb_tag_t
492hb_script_to_iso15924_tag (hb_script_t script)
493{
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400494 return (hb_tag_t) script;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400495}
496
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400497/**
498 * hb_script_get_horizontal_direction:
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430499 * @script:
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400500 *
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400501 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430502 *
503 * Return value:
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400504 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430505 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400506 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400507hb_direction_t
508hb_script_get_horizontal_direction (hb_script_t script)
509{
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +0430510 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400511 switch ((hb_tag_t) script)
512 {
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500513 /* Unicode-1.1 additions */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400514 case HB_SCRIPT_ARABIC:
515 case HB_SCRIPT_HEBREW:
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500516
517 /* Unicode-3.0 additions */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400518 case HB_SCRIPT_SYRIAC:
519 case HB_SCRIPT_THAANA:
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400520
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400521 /* Unicode-4.0 additions */
522 case HB_SCRIPT_CYPRIOT:
523
Behdad Esfahbod50e810c2012-03-07 12:49:08 -0500524 /* Unicode-4.1 additions */
525 case HB_SCRIPT_KHAROSHTHI:
526
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400527 /* Unicode-5.0 additions */
528 case HB_SCRIPT_PHOENICIAN:
529 case HB_SCRIPT_NKO:
530
Behdad Esfahbod50e810c2012-03-07 12:49:08 -0500531 /* Unicode-5.1 additions */
532 case HB_SCRIPT_LYDIAN:
533
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400534 /* Unicode-5.2 additions */
535 case HB_SCRIPT_AVESTAN:
536 case HB_SCRIPT_IMPERIAL_ARAMAIC:
537 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
538 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
539 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
540 case HB_SCRIPT_OLD_TURKIC:
541 case HB_SCRIPT_SAMARITAN:
542
543 /* Unicode-6.0 additions */
544 case HB_SCRIPT_MANDAIC:
545
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500546 /* Unicode-6.1 additions */
547 case HB_SCRIPT_MEROITIC_CURSIVE:
548 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
549
Behdad Esfahboda4a78992014-04-28 15:06:42 -0700550 /* Unicode-7.0 additions */
551 case HB_SCRIPT_MANICHAEAN:
552 case HB_SCRIPT_MENDE_KIKAKUI:
553 case HB_SCRIPT_NABATAEAN:
554 case HB_SCRIPT_OLD_NORTH_ARABIAN:
555 case HB_SCRIPT_PALMYRENE:
556 case HB_SCRIPT_PSALTER_PAHLAVI:
557
Behdad Esfahbod64a27262015-07-15 01:36:39 +0100558 /* Unicode-8.0 additions */
Cosimo Lupoc8f2a4f2018-01-18 22:49:40 +0100559 case HB_SCRIPT_HATRAN:
Behdad Esfahbod64a27262015-07-15 01:36:39 +0100560
Behdad Esfahbod691086f2016-05-06 12:08:18 +0100561 /* Unicode-9.0 additions */
562 case HB_SCRIPT_ADLAM:
563
Behdad Esfahbod060e6b42018-06-05 17:31:46 -0700564 /* Unicode-11.0 additions */
565 case HB_SCRIPT_HANIFI_ROHINGYA:
566 case HB_SCRIPT_OLD_SOGDIAN:
567 case HB_SCRIPT_SOGDIAN:
568
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400569 return HB_DIRECTION_RTL;
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700570
571
572 /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
David Corbettd8d1e7d2018-09-17 11:09:51 -0400573 case HB_SCRIPT_OLD_HUNGARIAN:
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700574 case HB_SCRIPT_OLD_ITALIC:
David Corbett46d8f0d2018-07-06 15:47:03 -0400575 case HB_SCRIPT_RUNIC:
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700576
577 return HB_DIRECTION_INVALID;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400578 }
579
580 return HB_DIRECTION_LTR;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400581}
582
583
Behdad Esfahbod218e67b2011-05-05 15:28:37 -0400584/* hb_user_data_array_t */
585
586bool
587hb_user_data_array_t::set (hb_user_data_key_t *key,
588 void * data,
Behdad Esfahbod33ccc772011-08-09 00:43:24 +0200589 hb_destroy_func_t destroy,
Behdad Esfahbod7babfe52012-12-04 00:35:54 +0200590 hb_bool_t replace)
Behdad Esfahbod218e67b2011-05-05 15:28:37 -0400591{
592 if (!key)
593 return false;
Behdad Esfahbod46df6822011-05-05 15:33:19 -0400594
Behdad Esfahbod33ccc772011-08-09 00:43:24 +0200595 if (replace) {
596 if (!data && !destroy) {
Behdad Esfahbod0e253e92012-06-05 15:37:19 -0400597 items.remove (key, lock);
Behdad Esfahbod33ccc772011-08-09 00:43:24 +0200598 return true;
599 }
Behdad Esfahbod218e67b2011-05-05 15:28:37 -0400600 }
601 hb_user_data_item_t item = {key, data, destroy};
Behdad Esfahbodea512f72015-11-26 19:22:22 -0500602 bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
Behdad Esfahbod46df6822011-05-05 15:33:19 -0400603
604 return ret;
Behdad Esfahbod218e67b2011-05-05 15:28:37 -0400605}
606
607void *
Behdad Esfahbod7babfe52012-12-04 00:35:54 +0200608hb_user_data_array_t::get (hb_user_data_key_t *key)
Behdad Esfahbod218e67b2011-05-05 15:28:37 -0400609{
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200610 hb_user_data_item_t item = {nullptr, nullptr, nullptr};
Behdad Esfahbod46df6822011-05-05 15:33:19 -0400611
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200612 return items.find (key, &item, lock) ? item.data : nullptr;
Behdad Esfahbod45bfa992011-05-10 19:12:49 -0400613}
Behdad Esfahbod46df6822011-05-05 15:33:19 -0400614
Behdad Esfahbod218e67b2011-05-05 15:28:37 -0400615
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400616/* hb_version */
617
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400618/**
619 * hb_version:
620 * @major: (out): Library major version component.
621 * @minor: (out): Library minor version component.
622 * @micro: (out): Library micro version component.
623 *
624 * Returns library version as three integer components.
625 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430626 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400627 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400628void
629hb_version (unsigned int *major,
630 unsigned int *minor,
631 unsigned int *micro)
632{
633 *major = HB_VERSION_MAJOR;
634 *minor = HB_VERSION_MINOR;
635 *micro = HB_VERSION_MICRO;
636}
637
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400638/**
639 * hb_version_string:
640 *
641 * Returns library version as a string with three components.
642 *
643 * Return value: library version string.
644 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430645 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400646 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400647const char *
648hb_version_string (void)
649{
650 return HB_VERSION_STRING;
651}
652
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400653/**
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400654 * hb_version_atleast:
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430655 * @major:
656 * @minor:
657 * @micro:
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400658 *
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400659 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430660 *
661 * Return value:
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400662 *
Sascha Brawer01c3a882015-06-01 13:22:01 +0200663 * Since: 0.9.30
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400664 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400665hb_bool_t
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400666hb_version_atleast (unsigned int major,
667 unsigned int minor,
668 unsigned int micro)
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400669{
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400670 return HB_VERSION_ATLEAST (major, minor, micro);
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400671}
Behdad Esfahbod72364102017-01-20 20:16:53 -0800672
673
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800674
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800675/* hb_feature_t and hb_variation_t */
Behdad Esfahbod72364102017-01-20 20:16:53 -0800676
677static bool
678parse_space (const char **pp, const char *end)
679{
680 while (*pp < end && ISSPACE (**pp))
681 (*pp)++;
682 return true;
683}
684
685static bool
686parse_char (const char **pp, const char *end, char c)
687{
688 parse_space (pp, end);
689
690 if (*pp == end || **pp != c)
691 return false;
692
693 (*pp)++;
694 return true;
695}
696
697static bool
698parse_uint (const char **pp, const char *end, unsigned int *pv)
699{
700 char buf[32];
701 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
702 strncpy (buf, *pp, len);
703 buf[len] = '\0';
704
705 char *p = buf;
706 char *pend = p;
707 unsigned int v;
708
709 /* Intentionally use strtol instead of strtoul, such that
710 * -1 turns into "big number"... */
711 errno = 0;
712 v = strtol (p, &pend, 0);
713 if (errno || p == pend)
714 return false;
715
716 *pv = v;
717 *pp += pend - p;
718 return true;
719}
720
721static bool
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430722parse_uint32 (const char **pp, const char *end, uint32_t *pv)
723{
724 char buf[32];
725 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
726 strncpy (buf, *pp, len);
727 buf[len] = '\0';
728
729 char *p = buf;
730 char *pend = p;
731 unsigned int v;
732
733 /* Intentionally use strtol instead of strtoul, such that
734 * -1 turns into "big number"... */
735 errno = 0;
736 v = strtol (p, &pend, 0);
737 if (errno || p == pend)
738 return false;
739
740 *pv = v;
741 *pp += pend - p;
742 return true;
743}
744
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400745#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
746#define USE_XLOCALE 1
Behdad Esfahbod7f39f572017-11-13 15:04:13 -0800747#define HB_LOCALE_T locale_t
748#define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
749#define HB_FREE_LOCALE(loc) freelocale (loc)
750#elif defined(_MSC_VER)
751#define USE_XLOCALE 1
752#define HB_LOCALE_T _locale_t
753#define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
754#define HB_FREE_LOCALE(loc) _free_locale (loc)
755#define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400756#endif
757
758#ifdef USE_XLOCALE
759
Emil A Eklund22defe02018-08-14 14:47:20 -0700760#ifdef HB_USE_ATEXIT
Behdad Esfahbod6750ec62018-08-12 17:42:16 -0700761static void free_static_C_locale (void);
Emil A Eklund22defe02018-08-14 14:47:20 -0700762#endif
Behdad Esfahbod6750ec62018-08-12 17:42:16 -0700763
764static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_ptr_t<HB_LOCALE_T>::value,
765 hb_C_locale_lazy_loader_t>
766{
767 static inline HB_LOCALE_T create (void)
768 {
769 HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400770
771#ifdef HB_USE_ATEXIT
Behdad Esfahbod6750ec62018-08-12 17:42:16 -0700772 atexit (free_static_C_locale);
773#endif
774
775 return C_locale;
776 }
777 static inline void destroy (HB_LOCALE_T p)
778 {
779 HB_FREE_LOCALE (p);
780 }
781 static inline HB_LOCALE_T get_null (void)
782 {
783 return nullptr;
784 }
785} static_C_locale;
786
787#ifdef HB_USE_ATEXIT
788static
789void free_static_C_locale (void)
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400790{
Behdad Esfahbod6750ec62018-08-12 17:42:16 -0700791 static_C_locale.free_instance ();
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400792}
793#endif
794
Behdad Esfahbod7f39f572017-11-13 15:04:13 -0800795static HB_LOCALE_T
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400796get_C_locale (void)
797{
Behdad Esfahbod6750ec62018-08-12 17:42:16 -0700798 return static_C_locale.get_unconst ();
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400799}
Behdad Esfahbod6750ec62018-08-12 17:42:16 -0700800#endif /* USE_XLOCALE */
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400801
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430802static bool
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800803parse_float (const char **pp, const char *end, float *pv)
804{
805 char buf[32];
806 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
807 strncpy (buf, *pp, len);
808 buf[len] = '\0';
809
810 char *p = buf;
811 char *pend = p;
812 float v;
813
814 errno = 0;
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400815#ifdef USE_XLOCALE
816 v = strtod_l (p, &pend, get_C_locale ());
817#else
Chun-wei Fan539571c2017-02-24 17:58:25 +0800818 v = strtod (p, &pend);
Behdad Esfahbod3ca69c82017-09-14 20:50:35 -0400819#endif
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800820 if (errno || p == pend)
821 return false;
822
823 *pv = v;
824 *pp += pend - p;
825 return true;
826}
827
828static bool
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430829parse_bool (const char **pp, const char *end, uint32_t *pv)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800830{
831 parse_space (pp, end);
832
833 const char *p = *pp;
834 while (*pp < end && ISALPHA(**pp))
835 (*pp)++;
836
837 /* CSS allows on/off as aliases 1/0. */
David Corbett85bb89a2017-12-04 15:15:27 -0500838 if (*pp - p == 2 && 0 == strncmp (p, "on", 2))
Behdad Esfahbod72364102017-01-20 20:16:53 -0800839 *pv = 1;
David Corbett85bb89a2017-12-04 15:15:27 -0500840 else if (*pp - p == 3 && 0 == strncmp (p, "off", 3))
Behdad Esfahbod72364102017-01-20 20:16:53 -0800841 *pv = 0;
842 else
843 return false;
844
845 return true;
846}
847
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800848/* hb_feature_t */
849
Behdad Esfahbod72364102017-01-20 20:16:53 -0800850static bool
851parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
852{
853 if (parse_char (pp, end, '-'))
854 feature->value = 0;
855 else {
856 parse_char (pp, end, '+');
857 feature->value = 1;
858 }
859
860 return true;
861}
862
863static bool
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800864parse_tag (const char **pp, const char *end, hb_tag_t *tag)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800865{
866 parse_space (pp, end);
867
868 char quote = 0;
869
870 if (*pp < end && (**pp == '\'' || **pp == '"'))
871 {
872 quote = **pp;
873 (*pp)++;
874 }
875
876 const char *p = *pp;
Martin Hosken39607dc2018-08-09 15:16:32 +0700877 while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
Behdad Esfahbod72364102017-01-20 20:16:53 -0800878 (*pp)++;
879
880 if (p == *pp || *pp - p > 4)
881 return false;
882
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800883 *tag = hb_tag_from_string (p, *pp - p);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800884
885 if (quote)
886 {
887 /* CSS expects exactly four bytes. And we only allow quotations for
888 * CSS compatibility. So, enforce the length. */
889 if (*pp - p != 4)
890 return false;
891 if (*pp == end || **pp != quote)
892 return false;
893 (*pp)++;
894 }
895
896 return true;
897}
898
899static bool
900parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
901{
902 parse_space (pp, end);
903
904 bool has_start;
905
Behdad Esfahbodbecd84a2018-09-11 01:26:18 +0200906 feature->start = HB_FEATURE_GLOBAL_START;
907 feature->end = HB_FEATURE_GLOBAL_END;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800908
909 if (!parse_char (pp, end, '['))
910 return true;
911
912 has_start = parse_uint (pp, end, &feature->start);
913
914 if (parse_char (pp, end, ':')) {
915 parse_uint (pp, end, &feature->end);
916 } else {
917 if (has_start)
918 feature->end = feature->start + 1;
919 }
920
921 return parse_char (pp, end, ']');
922}
923
924static bool
925parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
926{
927 bool had_equal = parse_char (pp, end, '=');
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430928 bool had_value = parse_uint32 (pp, end, &feature->value) ||
Behdad Esfahbod72364102017-01-20 20:16:53 -0800929 parse_bool (pp, end, &feature->value);
930 /* CSS doesn't use equal-sign between tag and value.
931 * If there was an equal-sign, then there *must* be a value.
Bruce Mitchener90218fa2018-01-31 20:44:45 +0700932 * A value without an equal-sign is ok, but not required. */
Behdad Esfahbod72364102017-01-20 20:16:53 -0800933 return !had_equal || had_value;
934}
935
Behdad Esfahbod72364102017-01-20 20:16:53 -0800936static bool
937parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
938{
939 return parse_feature_value_prefix (pp, end, feature) &&
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800940 parse_tag (pp, end, &feature->tag) &&
Behdad Esfahbod72364102017-01-20 20:16:53 -0800941 parse_feature_indices (pp, end, feature) &&
942 parse_feature_value_postfix (pp, end, feature) &&
943 parse_space (pp, end) &&
944 *pp == end;
945}
946
947/**
948 * hb_feature_from_string:
949 * @str: (array length=len) (element-type uint8_t): a string to parse
950 * @len: length of @str, or -1 if string is %NULL terminated
951 * @feature: (out): the #hb_feature_t to initialize with the parsed values
952 *
953 * Parses a string into a #hb_feature_t.
954 *
955 * TODO: document the syntax here.
956 *
957 * Return value:
958 * %true if @str is successfully parsed, %false otherwise.
959 *
960 * Since: 0.9.5
961 **/
962hb_bool_t
963hb_feature_from_string (const char *str, int len,
964 hb_feature_t *feature)
965{
966 hb_feature_t feat;
967
968 if (len < 0)
969 len = strlen (str);
970
971 if (likely (parse_one_feature (&str, str + len, &feat)))
972 {
973 if (feature)
974 *feature = feat;
975 return true;
976 }
977
978 if (feature)
979 memset (feature, 0, sizeof (*feature));
980 return false;
981}
982
983/**
984 * hb_feature_to_string:
985 * @feature: an #hb_feature_t to convert
986 * @buf: (array length=size) (out): output string
987 * @size: the allocated size of @buf
988 *
989 * Converts a #hb_feature_t into a %NULL-terminated string in the format
990 * understood by hb_feature_from_string(). The client in responsible for
991 * allocating big enough size for @buf, 128 bytes is more than enough.
992 *
993 * Since: 0.9.5
994 **/
995void
996hb_feature_to_string (hb_feature_t *feature,
997 char *buf, unsigned int size)
998{
999 if (unlikely (!size)) return;
1000
1001 char s[128];
1002 unsigned int len = 0;
1003 if (feature->value == 0)
1004 s[len++] = '-';
1005 hb_tag_to_string (feature->tag, s + len);
1006 len += 4;
1007 while (len && s[len - 1] == ' ')
1008 len--;
1009 if (feature->start != 0 || feature->end != (unsigned int) -1)
1010 {
1011 s[len++] = '[';
1012 if (feature->start)
1013 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
1014 if (feature->end != feature->start + 1) {
1015 s[len++] = ':';
1016 if (feature->end != (unsigned int) -1)
1017 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
1018 }
1019 s[len++] = ']';
1020 }
1021 if (feature->value > 1)
1022 {
1023 s[len++] = '=';
1024 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
1025 }
1026 assert (len < ARRAY_LENGTH (s));
1027 len = MIN (len, size - 1);
1028 memcpy (buf, s, len);
1029 buf[len] = '\0';
1030}
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001031
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001032/* hb_variation_t */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001033
1034static bool
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001035parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001036{
1037 parse_char (pp, end, '='); /* Optional. */
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001038 return parse_float (pp, end, &variation->value);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001039}
1040
1041static bool
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001042parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001043{
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001044 return parse_tag (pp, end, &variation->tag) &&
1045 parse_variation_value (pp, end, variation) &&
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001046 parse_space (pp, end) &&
1047 *pp == end;
1048}
1049
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001050/**
1051 * hb_variation_from_string:
1052 *
1053 * Since: 1.4.2
1054 */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001055hb_bool_t
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001056hb_variation_from_string (const char *str, int len,
1057 hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001058{
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001059 hb_variation_t var;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001060
1061 if (len < 0)
1062 len = strlen (str);
1063
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001064 if (likely (parse_one_variation (&str, str + len, &var)))
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001065 {
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001066 if (variation)
1067 *variation = var;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001068 return true;
1069 }
1070
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001071 if (variation)
1072 memset (variation, 0, sizeof (*variation));
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001073 return false;
1074}
1075
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001076/**
1077 * hb_variation_to_string:
1078 *
1079 * Since: 1.4.2
1080 */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001081void
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001082hb_variation_to_string (hb_variation_t *variation,
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001083 char *buf, unsigned int size)
1084{
1085 if (unlikely (!size)) return;
1086
1087 char s[128];
1088 unsigned int len = 0;
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001089 hb_tag_to_string (variation->tag, s + len);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001090 len += 4;
1091 while (len && s[len - 1] == ' ')
1092 len--;
1093 s[len++] = '=';
Ebrahim Byagowi5de2d9c2018-10-04 02:14:18 +03301094 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001095
1096 assert (len < ARRAY_LENGTH (s));
1097 len = MIN (len, size - 1);
1098 memcpy (buf, s, len);
1099 buf[len] = '\0';
1100}
Behdad Esfahbode22a48a2018-07-23 13:24:26 -07001101
1102/* If there is no visibility control, then hb-static.cc will NOT
1103 * define anything. Instead, we get it to define one set in here
1104 * only, so only libharfbuzz.so defines them, not other libs. */
1105#ifdef HB_NO_VISIBILITY
1106#undef HB_NO_VISIBILITY
1107#include "hb-static.cc"
1108#define HB_NO_VISIBILITY 1
1109#endif