blob: 26c8ad0f4974f09b7e3f69eb1b82ba2bd1455b0b [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>
Behdad Esfahbod34fb5522011-05-06 00:04:28 -040033
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 Esfahboded116322021-09-14 07:09:54 -0400283static inline void
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330284free_langs ()
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400285{
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700286retry:
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500287 hb_language_item_t *first_lang = langs;
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700288 if (unlikely (!langs.cmpexch (first_lang, nullptr)))
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700289 goto retry;
290
291 while (first_lang) {
292 hb_language_item_t *next = first_lang->next;
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400293 first_lang->fini ();
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600294 hb_free (first_lang);
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700295 first_lang = next;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400296 }
297}
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400298
299static hb_language_item_t *
300lang_find_or_insert (const char *key)
301{
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400302retry:
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500303 hb_language_item_t *first_lang = langs;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400304
305 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
306 if (*lang == key)
307 return lang;
308
309 /* Not found; allocate one. */
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600310 hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400311 if (unlikely (!lang))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200312 return nullptr;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400313 lang->next = first_lang;
314 *lang = key;
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400315 if (unlikely (!lang->lang))
316 {
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600317 hb_free (lang);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200318 return nullptr;
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400319 }
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400320
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700321 if (unlikely (!langs.cmpexch (first_lang, lang)))
322 {
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400323 lang->fini ();
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600324 hb_free (lang);
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400325 goto retry;
326 }
327
Behdad Esfahbod04aed572012-06-05 18:30:19 -0400328 if (!first_lang)
Behdad Esfahboded116322021-09-14 07:09:54 -0400329 hb_atexit (free_langs); /* First person registers atexit() callback. */
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400330
331 return lang;
332}
333
Behdad Esfahbodb8d61832011-05-05 15:14:04 -0400334
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400335/**
336 * hb_language_from_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400337 * @str: (array length=len) (element-type uint8_t): a string representing
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200338 * a BCP 47 language tag
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400339 * @len: length of the @str, or -1 if it is %NULL-terminated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400340 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200341 * Converts @str representing a BCP 47 language tag to the corresponding
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400342 * #hb_language_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400343 *
Khaled Hosny04f89e82015-04-10 17:49:01 +0200344 * Return value: (transfer none):
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200345 * The #hb_language_t corresponding to the BCP 47 language tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400346 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430347 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400348 **/
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400349hb_language_t
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200350hb_language_from_string (const char *str, int len)
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400351{
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200352 if (!str || !len || !*str)
Behdad Esfahbod1a64f6e2011-05-13 22:55:32 -0400353 return HB_LANGUAGE_INVALID;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400354
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200355 hb_language_item_t *item = nullptr;
Behdad Esfahbod48360ec2013-09-26 16:48:42 -0400356 if (len >= 0)
357 {
Behdad Esfahboddac86022014-06-03 17:57:00 -0400358 /* NUL-terminate it. */
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100359 char strbuf[64];
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700360 len = hb_min (len, (int) sizeof (strbuf) - 1);
Behdad Esfahboddac86022014-06-03 17:57:00 -0400361 memcpy (strbuf, str, len);
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200362 strbuf[len] = '\0';
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100363 item = lang_find_or_insert (strbuf);
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200364 }
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100365 else
366 item = lang_find_or_insert (str);
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400367
Behdad Esfahbod1a64f6e2011-05-13 22:55:32 -0400368 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400369}
370
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400371/**
372 * hb_language_to_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100373 * @language: The #hb_language_t to convert
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400374 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100375 * Converts an #hb_language_t to a string.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400376 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400377 * Return value: (transfer none):
378 * A %NULL-terminated string representing the @language. Must not be freed by
379 * the caller.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400380 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430381 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400382 **/
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400383const char *
384hb_language_to_string (hb_language_t language)
385{
Ebrahim Byagowie6909ee2019-09-18 22:12:25 +0430386 if (unlikely (!language)) return nullptr;
Ebrahim Byagowid8af9ee2019-09-18 00:47:55 +0430387
Behdad Esfahbod3cbdf702011-04-15 12:32:06 -0400388 return language->s;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400389}
390
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400391/**
392 * hb_language_get_default:
393 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100394 * Fetch the default language from current locale.
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430395 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100396 * <note>Note that the first time this function is called, it calls
Behdad Esfahbodba0f0f12018-09-30 03:49:52 -0400397 * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying
398 * setlocale function is, in many implementations, NOT threadsafe. To avoid
399 * problems, call this function once before multiple threads can call it.
400 * This function is only used from hb_buffer_guess_segment_properties() by
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100401 * HarfBuzz itself.</note>
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400402 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100403 * Return value: (transfer none): The default language of the locale as
404 * an #hb_language_t
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400405 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430406 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400407 **/
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400408hb_language_t
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330409hb_language_get_default ()
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400410{
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700411 static hb_atomic_ptr_t <hb_language_t> default_language;
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400412
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500413 hb_language_t language = default_language;
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700414 if (unlikely (language == HB_LANGUAGE_INVALID))
415 {
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200416 language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700417 (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400418 }
419
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700420 return language;
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400421}
422
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400423
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400424/* hb_script_t */
425
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400426/**
427 * hb_script_from_iso15924_tag:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200428 * @tag: an #hb_tag_t representing an ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400429 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200430 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400431 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430432 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200433 * An #hb_script_t corresponding to the ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400434 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430435 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400436 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400437hb_script_t
438hb_script_from_iso15924_tag (hb_tag_t tag)
439{
Behdad Esfahbodf144a8e2011-04-20 02:54:42 -0400440 if (unlikely (tag == HB_TAG_NONE))
441 return HB_SCRIPT_INVALID;
442
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400443 /* Be lenient, adjust case (one capital letter followed by three small letters) */
Behdad Esfahbod76271002014-07-11 14:54:42 -0400444 tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400445
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400446 switch (tag) {
Behdad Esfahbodd02985e2011-05-02 12:35:14 -0400447
448 /* These graduated from the 'Q' private-area codes, but
449 * the old code is still aliased by Unicode, and the Qaai
450 * one in use by ICU. */
451 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
452 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
453
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +0430454 /* Script variants from https://unicode.org/iso15924/ */
David Corbett3bd43bd2020-11-16 21:55:02 -0500455 case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400456 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
David Corbett3bd43bd2020-11-16 21:55:02 -0500457 case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;
458 case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;
459 case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;
460 case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400461 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
462 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
463 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
464 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
465 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
466 }
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400467
468 /* If it looks right, just use the tag as a script */
Behdad Esfahbod76271002014-07-11 14:54:42 -0400469 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400470 return (hb_script_t) tag;
471
472 /* Otherwise, return unknown */
473 return HB_SCRIPT_UNKNOWN;
474}
475
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400476/**
477 * hb_script_from_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400478 * @str: (array length=len) (element-type uint8_t): a string representing an
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200479 * ISO 15924 tag.
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400480 * @len: length of the @str, or -1 if it is %NULL-terminated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400481 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200482 * Converts a string @str representing an ISO 15924 script tag to a
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400483 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
484 * hb_script_from_iso15924_tag().
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400485 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430486 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200487 * An #hb_script_t corresponding to the ISO 15924 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_script_t
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400492hb_script_from_string (const char *str, int len)
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400493{
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400494 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400495}
496
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400497/**
498 * hb_script_to_iso15924_tag:
David Corbett5daeff32019-04-17 09:11:44 -0400499 * @script: an #hb_script_t to convert.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400500 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100501 * Converts an #hb_script_t to a corresponding ISO 15924 script tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400502 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400503 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200504 * An #hb_tag_t representing an ISO 15924 script tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400505 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430506 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400507 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400508hb_tag_t
509hb_script_to_iso15924_tag (hb_script_t script)
510{
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400511 return (hb_tag_t) script;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400512}
513
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400514/**
515 * hb_script_get_horizontal_direction:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100516 * @script: The #hb_script_t to query
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400517 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100518 * Fetches the #hb_direction_t of a script when it is
519 * set horizontally. All right-to-left scripts will return
520 * #HB_DIRECTION_RTL. All left-to-right scripts will return
Khaled Hosny8586f152020-12-24 22:23:47 +0200521 * #HB_DIRECTION_LTR. Scripts that can be written either
522 * horizontally or vertically will return #HB_DIRECTION_INVALID.
523 * Unknown scripts will return #HB_DIRECTION_LTR.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400524 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100525 * Return value: The horizontal #hb_direction_t of @script
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400526 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430527 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400528 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400529hb_direction_t
530hb_script_get_horizontal_direction (hb_script_t script)
531{
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +0430532 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400533 switch ((hb_tag_t) script)
534 {
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500535 /* Unicode-1.1 additions */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400536 case HB_SCRIPT_ARABIC:
537 case HB_SCRIPT_HEBREW:
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500538
539 /* Unicode-3.0 additions */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400540 case HB_SCRIPT_SYRIAC:
541 case HB_SCRIPT_THAANA:
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400542
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400543 /* Unicode-4.0 additions */
544 case HB_SCRIPT_CYPRIOT:
545
Behdad Esfahbod50e810c2012-03-07 12:49:08 -0500546 /* Unicode-4.1 additions */
547 case HB_SCRIPT_KHAROSHTHI:
548
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400549 /* Unicode-5.0 additions */
550 case HB_SCRIPT_PHOENICIAN:
551 case HB_SCRIPT_NKO:
552
Behdad Esfahbod50e810c2012-03-07 12:49:08 -0500553 /* Unicode-5.1 additions */
554 case HB_SCRIPT_LYDIAN:
555
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400556 /* Unicode-5.2 additions */
557 case HB_SCRIPT_AVESTAN:
558 case HB_SCRIPT_IMPERIAL_ARAMAIC:
559 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
560 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
561 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
562 case HB_SCRIPT_OLD_TURKIC:
563 case HB_SCRIPT_SAMARITAN:
564
565 /* Unicode-6.0 additions */
566 case HB_SCRIPT_MANDAIC:
567
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500568 /* Unicode-6.1 additions */
569 case HB_SCRIPT_MEROITIC_CURSIVE:
570 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
571
Behdad Esfahboda4a78992014-04-28 15:06:42 -0700572 /* Unicode-7.0 additions */
573 case HB_SCRIPT_MANICHAEAN:
574 case HB_SCRIPT_MENDE_KIKAKUI:
575 case HB_SCRIPT_NABATAEAN:
576 case HB_SCRIPT_OLD_NORTH_ARABIAN:
577 case HB_SCRIPT_PALMYRENE:
578 case HB_SCRIPT_PSALTER_PAHLAVI:
579
Behdad Esfahbod64a27262015-07-15 01:36:39 +0100580 /* Unicode-8.0 additions */
Cosimo Lupoc8f2a4f2018-01-18 22:49:40 +0100581 case HB_SCRIPT_HATRAN:
Behdad Esfahbod64a27262015-07-15 01:36:39 +0100582
Behdad Esfahbod691086f2016-05-06 12:08:18 +0100583 /* Unicode-9.0 additions */
584 case HB_SCRIPT_ADLAM:
585
Behdad Esfahbod060e6b42018-06-05 17:31:46 -0700586 /* Unicode-11.0 additions */
587 case HB_SCRIPT_HANIFI_ROHINGYA:
588 case HB_SCRIPT_OLD_SOGDIAN:
589 case HB_SCRIPT_SOGDIAN:
590
David Corbett665483c2020-04-01 17:28:12 -0400591 /* Unicode-12.0 additions */
592 case HB_SCRIPT_ELYMAIC:
593
David Corbettfd748fa2020-03-15 15:59:31 -0400594 /* Unicode-13.0 additions */
595 case HB_SCRIPT_CHORASMIAN:
596 case HB_SCRIPT_YEZIDI:
597
David Corbett7b05eec2021-09-14 17:51:11 -0400598 /* Unicode-14.0 additions */
599 case HB_SCRIPT_OLD_UYGHUR:
600
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400601 return HB_DIRECTION_RTL;
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700602
603
604 /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
David Corbettd8d1e7d2018-09-17 11:09:51 -0400605 case HB_SCRIPT_OLD_HUNGARIAN:
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700606 case HB_SCRIPT_OLD_ITALIC:
David Corbett46d8f0d2018-07-06 15:47:03 -0400607 case HB_SCRIPT_RUNIC:
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700608
609 return HB_DIRECTION_INVALID;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400610 }
611
612 return HB_DIRECTION_LTR;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400613}
614
615
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400616/* hb_version */
617
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -0700618
619/**
620 * SECTION:hb-version
Behdad Esfahbodcf5fa572018-10-27 04:50:38 -0700621 * @title: hb-version
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -0700622 * @short_description: Information about the version of HarfBuzz in use
623 * @include: hb.h
624 *
625 * These functions and macros allow accessing version of the HarfBuzz
626 * library used at compile- as well as run-time, and to direct code
627 * conditionally based on those versions, again, at compile- or run-time.
628 **/
629
630
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400631/**
632 * hb_version:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100633 * @major: (out): Library major version component
634 * @minor: (out): Library minor version component
635 * @micro: (out): Library micro version component
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400636 *
637 * Returns library version as three integer components.
638 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430639 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400640 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400641void
642hb_version (unsigned int *major,
643 unsigned int *minor,
644 unsigned int *micro)
645{
646 *major = HB_VERSION_MAJOR;
647 *minor = HB_VERSION_MINOR;
648 *micro = HB_VERSION_MICRO;
649}
650
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400651/**
652 * hb_version_string:
653 *
654 * Returns library version as a string with three components.
655 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100656 * Return value: Library version string
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400657 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430658 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400659 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400660const char *
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330661hb_version_string ()
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400662{
663 return HB_VERSION_STRING;
664}
665
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400666/**
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400667 * hb_version_atleast:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100668 * @major: Library major version component
669 * @minor: Library minor version component
670 * @micro: Library micro version component
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400671 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100672 * Tests the library version against a minimum value,
673 * as three integer components.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400674 *
Khaled Hosnya8e72ee2020-12-30 23:08:40 +0200675 * Return value: %true if the library is equal to or greater than
676 * the test value, %false otherwise
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400677 *
Sascha Brawer01c3a882015-06-01 13:22:01 +0200678 * Since: 0.9.30
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400679 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400680hb_bool_t
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400681hb_version_atleast (unsigned int major,
682 unsigned int minor,
683 unsigned int micro)
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400684{
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400685 return HB_VERSION_ATLEAST (major, minor, micro);
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400686}
Behdad Esfahbod72364102017-01-20 20:16:53 -0800687
688
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800689
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800690/* hb_feature_t and hb_variation_t */
Behdad Esfahbod72364102017-01-20 20:16:53 -0800691
692static bool
693parse_space (const char **pp, const char *end)
694{
695 while (*pp < end && ISSPACE (**pp))
696 (*pp)++;
697 return true;
698}
699
700static bool
701parse_char (const char **pp, const char *end, char c)
702{
703 parse_space (pp, end);
704
705 if (*pp == end || **pp != c)
706 return false;
707
708 (*pp)++;
709 return true;
710}
711
712static bool
713parse_uint (const char **pp, const char *end, unsigned int *pv)
714{
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430715 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
716 * such that -1 turns into "big number"... */
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430717 int v;
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430718 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800719
720 *pv = v;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800721 return true;
722}
723
724static bool
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430725parse_uint32 (const char **pp, const char *end, uint32_t *pv)
726{
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430727 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
728 * such that -1 turns into "big number"... */
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430729 int v;
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430730 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430731
732 *pv = v;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800733 return true;
734}
735
736static bool
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430737parse_bool (const char **pp, const char *end, uint32_t *pv)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800738{
739 parse_space (pp, end);
740
741 const char *p = *pp;
742 while (*pp < end && ISALPHA(**pp))
743 (*pp)++;
744
745 /* CSS allows on/off as aliases 1/0. */
David Corbett45adc182019-02-18 22:30:40 -0500746 if (*pp - p == 2
747 && TOLOWER (p[0]) == 'o'
748 && TOLOWER (p[1]) == 'n')
Behdad Esfahbod72364102017-01-20 20:16:53 -0800749 *pv = 1;
David Corbett45adc182019-02-18 22:30:40 -0500750 else if (*pp - p == 3
751 && TOLOWER (p[0]) == 'o'
752 && TOLOWER (p[1]) == 'f'
753 && TOLOWER (p[2]) == 'f')
Behdad Esfahbod72364102017-01-20 20:16:53 -0800754 *pv = 0;
755 else
756 return false;
757
758 return true;
759}
760
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800761/* hb_feature_t */
762
Behdad Esfahbod72364102017-01-20 20:16:53 -0800763static bool
764parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
765{
766 if (parse_char (pp, end, '-'))
767 feature->value = 0;
768 else {
769 parse_char (pp, end, '+');
770 feature->value = 1;
771 }
772
773 return true;
774}
775
776static bool
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800777parse_tag (const char **pp, const char *end, hb_tag_t *tag)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800778{
779 parse_space (pp, end);
780
781 char quote = 0;
782
783 if (*pp < end && (**pp == '\'' || **pp == '"'))
784 {
785 quote = **pp;
786 (*pp)++;
787 }
788
789 const char *p = *pp;
Martin Hosken39607dc2018-08-09 15:16:32 +0700790 while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
Behdad Esfahbod72364102017-01-20 20:16:53 -0800791 (*pp)++;
792
793 if (p == *pp || *pp - p > 4)
794 return false;
795
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800796 *tag = hb_tag_from_string (p, *pp - p);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800797
798 if (quote)
799 {
800 /* CSS expects exactly four bytes. And we only allow quotations for
801 * CSS compatibility. So, enforce the length. */
802 if (*pp - p != 4)
803 return false;
804 if (*pp == end || **pp != quote)
805 return false;
806 (*pp)++;
807 }
808
809 return true;
810}
811
812static bool
813parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
814{
815 parse_space (pp, end);
816
817 bool has_start;
818
Behdad Esfahbodbecd84a2018-09-11 01:26:18 +0200819 feature->start = HB_FEATURE_GLOBAL_START;
820 feature->end = HB_FEATURE_GLOBAL_END;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800821
822 if (!parse_char (pp, end, '['))
823 return true;
824
825 has_start = parse_uint (pp, end, &feature->start);
826
Behdad Esfahbod88694362018-10-23 03:07:48 -0700827 if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
Behdad Esfahbod72364102017-01-20 20:16:53 -0800828 parse_uint (pp, end, &feature->end);
829 } else {
830 if (has_start)
831 feature->end = feature->start + 1;
832 }
833
834 return parse_char (pp, end, ']');
835}
836
837static bool
838parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
839{
840 bool had_equal = parse_char (pp, end, '=');
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430841 bool had_value = parse_uint32 (pp, end, &feature->value) ||
Ebrahim Byagowia0b4ac42019-08-24 17:57:14 +0430842 parse_bool (pp, end, &feature->value);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800843 /* CSS doesn't use equal-sign between tag and value.
844 * If there was an equal-sign, then there *must* be a value.
Bruce Mitchener90218fa2018-01-31 20:44:45 +0700845 * A value without an equal-sign is ok, but not required. */
Behdad Esfahbod72364102017-01-20 20:16:53 -0800846 return !had_equal || had_value;
847}
848
Behdad Esfahbod72364102017-01-20 20:16:53 -0800849static bool
850parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
851{
852 return parse_feature_value_prefix (pp, end, feature) &&
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800853 parse_tag (pp, end, &feature->tag) &&
Behdad Esfahbod72364102017-01-20 20:16:53 -0800854 parse_feature_indices (pp, end, feature) &&
855 parse_feature_value_postfix (pp, end, feature) &&
856 parse_space (pp, end) &&
857 *pp == end;
858}
859
860/**
861 * hb_feature_from_string:
862 * @str: (array length=len) (element-type uint8_t): a string to parse
863 * @len: length of @str, or -1 if string is %NULL terminated
864 * @feature: (out): the #hb_feature_t to initialize with the parsed values
865 *
866 * Parses a string into a #hb_feature_t.
867 *
Khaled Hosny6bd4c082019-02-19 02:23:58 +0200868 * The format for specifying feature strings follows. All valid CSS
David Corbett45adc182019-02-18 22:30:40 -0500869 * font-feature-settings values other than 'normal' and the global values are
870 * also accepted, though not documented below. CSS string escapes are not
871 * supported.
Khaled Hosny6bd4c082019-02-19 02:23:58 +0200872 *
873 * The range indices refer to the positions between Unicode characters. The
874 * position before the first character is always 0.
875 *
876 * The format is Python-esque. Here is how it all works:
877 *
878 * <informaltable pgwide='1' align='left' frame='none'>
879 * <tgroup cols='5'>
880 * <thead>
881 * <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
882 * </thead>
883 * <tbody>
884 * <row><entry>Setting value:</entry></row>
885 * <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
886 * <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
887 * <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
888 * <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
889 * <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
890 * <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>
891 * <row><entry>Setting index:</entry></row>
892 * <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
893 * <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
894 * <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>
895 * <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>
896 * <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>
897 * <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
898 * <row><entry>Mixing it all:</entry></row>
899 * <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>
900 * </tbody>
901 * </tgroup>
902 * </informaltable>
Behdad Esfahbod72364102017-01-20 20:16:53 -0800903 *
904 * Return value:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100905 * %true if @str is successfully parsed, %false otherwise
Behdad Esfahbod72364102017-01-20 20:16:53 -0800906 *
907 * Since: 0.9.5
908 **/
909hb_bool_t
910hb_feature_from_string (const char *str, int len,
911 hb_feature_t *feature)
912{
913 hb_feature_t feat;
914
915 if (len < 0)
916 len = strlen (str);
917
918 if (likely (parse_one_feature (&str, str + len, &feat)))
919 {
920 if (feature)
921 *feature = feat;
922 return true;
923 }
924
925 if (feature)
926 memset (feature, 0, sizeof (*feature));
927 return false;
928}
929
930/**
931 * hb_feature_to_string:
932 * @feature: an #hb_feature_t to convert
933 * @buf: (array length=size) (out): output string
934 * @size: the allocated size of @buf
935 *
936 * Converts a #hb_feature_t into a %NULL-terminated string in the format
937 * understood by hb_feature_from_string(). The client in responsible for
938 * allocating big enough size for @buf, 128 bytes is more than enough.
939 *
940 * Since: 0.9.5
941 **/
942void
943hb_feature_to_string (hb_feature_t *feature,
944 char *buf, unsigned int size)
945{
946 if (unlikely (!size)) return;
947
948 char s[128];
949 unsigned int len = 0;
950 if (feature->value == 0)
951 s[len++] = '-';
952 hb_tag_to_string (feature->tag, s + len);
953 len += 4;
954 while (len && s[len - 1] == ' ')
955 len--;
Evgeniy Reiznerb79ceac2019-12-15 16:50:01 +0200956 if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800957 {
958 s[len++] = '[';
959 if (feature->start)
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700960 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
Behdad Esfahbod72364102017-01-20 20:16:53 -0800961 if (feature->end != feature->start + 1) {
962 s[len++] = ':';
Evgeniy Reiznerb79ceac2019-12-15 16:50:01 +0200963 if (feature->end != HB_FEATURE_GLOBAL_END)
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700964 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
Behdad Esfahbod72364102017-01-20 20:16:53 -0800965 }
966 s[len++] = ']';
967 }
968 if (feature->value > 1)
969 {
970 s[len++] = '=';
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700971 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
Behdad Esfahbod72364102017-01-20 20:16:53 -0800972 }
973 assert (len < ARRAY_LENGTH (s));
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700974 len = hb_min (len, size - 1);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800975 memcpy (buf, s, len);
976 buf[len] = '\0';
977}
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800978
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800979/* hb_variation_t */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800980
981static bool
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800982parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800983{
984 parse_char (pp, end, '='); /* Optional. */
Ebrahim Byagowi57f88e12019-09-04 01:20:50 +0430985 double v;
986 if (unlikely (!hb_parse_double (pp, end, &v))) return false;
987
988 variation->value = v;
989 return true;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800990}
991
992static bool
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800993parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800994{
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800995 return parse_tag (pp, end, &variation->tag) &&
996 parse_variation_value (pp, end, variation) &&
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800997 parse_space (pp, end) &&
998 *pp == end;
999}
1000
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001001/**
1002 * hb_variation_from_string:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001003 * @str: (array length=len) (element-type uint8_t): a string to parse
1004 * @len: length of @str, or -1 if string is %NULL terminated
1005 * @variation: (out): the #hb_variation_t to initialize with the parsed values
1006 *
1007 * Parses a string into a #hb_variation_t.
1008 *
1009 * The format for specifying variation settings follows. All valid CSS
1010 * font-variation-settings values other than 'normal' and 'inherited' are also
1011 * accepted, though, not documented below.
1012 *
1013 * The format is a tag, optionally followed by an equals sign, followed by a
1014 * number. For example `wght=500`, or `slnt=-7.5`.
1015 *
1016 * Return value:
1017 * %true if @str is successfully parsed, %false otherwise
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001018 *
1019 * Since: 1.4.2
1020 */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001021hb_bool_t
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001022hb_variation_from_string (const char *str, int len,
1023 hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001024{
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001025 hb_variation_t var;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001026
1027 if (len < 0)
1028 len = strlen (str);
1029
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001030 if (likely (parse_one_variation (&str, str + len, &var)))
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001031 {
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001032 if (variation)
1033 *variation = var;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001034 return true;
1035 }
1036
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001037 if (variation)
1038 memset (variation, 0, sizeof (*variation));
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001039 return false;
1040}
1041
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001042/**
1043 * hb_variation_to_string:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001044 * @variation: an #hb_variation_t to convert
1045 * @buf: (array length=size) (out): output string
1046 * @size: the allocated size of @buf
1047 *
1048 * Converts an #hb_variation_t into a %NULL-terminated string in the format
1049 * understood by hb_variation_from_string(). The client in responsible for
1050 * allocating big enough size for @buf, 128 bytes is more than enough.
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001051 *
1052 * Since: 1.4.2
1053 */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001054void
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001055hb_variation_to_string (hb_variation_t *variation,
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001056 char *buf, unsigned int size)
1057{
1058 if (unlikely (!size)) return;
1059
1060 char s[128];
1061 unsigned int len = 0;
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001062 hb_tag_to_string (variation->tag, s + len);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001063 len += 4;
1064 while (len && s[len - 1] == ' ')
1065 len--;
1066 s[len++] = '=';
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001067 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001068
1069 assert (len < ARRAY_LENGTH (s));
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001070 len = hb_min (len, size - 1);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001071 memcpy (buf, s, len);
1072 buf[len] = '\0';
1073}
Behdad Esfahbode22a48a2018-07-23 13:24:26 -07001074
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001075/**
1076 * hb_color_get_alpha:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001077 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001078 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001079 * Fetches the alpha channel of the given @color.
1080 *
1081 * Return value: Alpha channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001082 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001083 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001084 */
1085uint8_t
1086(hb_color_get_alpha) (hb_color_t color)
1087{
1088 return hb_color_get_alpha (color);
1089}
1090
1091/**
1092 * hb_color_get_red:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001093 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001094 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001095 * Fetches the red channel of the given @color.
1096 *
1097 * Return value: Red channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001098 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001099 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001100 */
1101uint8_t
1102(hb_color_get_red) (hb_color_t color)
1103{
1104 return hb_color_get_red (color);
1105}
1106
1107/**
1108 * hb_color_get_green:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001109 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001110 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001111 * Fetches the green channel of the given @color.
1112 *
1113 * Return value: Green channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001114 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001115 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001116 */
1117uint8_t
1118(hb_color_get_green) (hb_color_t color)
1119{
1120 return hb_color_get_green (color);
1121}
1122
1123/**
1124 * hb_color_get_blue:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001125 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001126 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001127 * Fetches the blue channel of the given @color.
1128 *
1129 * Return value: Blue channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001130 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001131 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001132 */
1133uint8_t
1134(hb_color_get_blue) (hb_color_t color)
1135{
1136 return hb_color_get_blue (color);
1137}
1138
1139
Behdad Esfahbode22a48a2018-07-23 13:24:26 -07001140/* If there is no visibility control, then hb-static.cc will NOT
1141 * define anything. Instead, we get it to define one set in here
1142 * only, so only libharfbuzz.so defines them, not other libs. */
1143#ifdef HB_NO_VISIBILITY
1144#undef HB_NO_VISIBILITY
1145#include "hb-static.cc"
1146#define HB_NO_VISIBILITY 1
1147#endif