blob: e9f9cfeb5ff59ea6477ec0927d718ed10e3ad7c1 [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 Esfahbod91b779e2022-01-28 13:52:15 -070032#if !defined(HB_NO_SETLOCALE) && (!defined(HAVE_NEWLOCALE) || !defined(HAVE_USELOCALE))
Behdad Esfahbod13643932022-01-12 10:54:28 -070033#define HB_NO_SETLOCALE 1
34#endif
Behdad Esfahbod34fb5522011-05-06 00:04:28 -040035
Behdad Esfahbod13643932022-01-12 10:54:28 -070036#ifndef HB_NO_SETLOCALE
37
38#include <locale.h>
Behdad Esfahboda45a6302022-01-08 15:47:33 -080039#ifdef HAVE_XLOCALE_H
40#include <xlocale.h> // Needed on BSD/OS X for uselocale
41#endif
42
Behdad Esfahbod13643932022-01-12 10:54:28 -070043#ifdef WIN32
Behdad Esfahbod589bea12022-01-15 17:55:10 -070044#define hb_locale_t _locale_t
Behdad Esfahbod13643932022-01-12 10:54:28 -070045#else
Behdad Esfahbod589bea12022-01-15 17:55:10 -070046#define hb_locale_t locale_t
Behdad Esfahbod13643932022-01-12 10:54:28 -070047#endif
Behdad Esfahbodb97e4f72022-01-15 17:47:51 -070048#define hb_setlocale setlocale
49#define hb_uselocale uselocale
Behdad Esfahbod13643932022-01-12 10:54:28 -070050
51#else
52
Behdad Esfahbod13643932022-01-12 10:54:28 -070053#define hb_locale_t void *
Behdad Esfahbodb97e4f72022-01-15 17:47:51 -070054#define hb_setlocale(Category, Locale) "C"
55#define hb_uselocale(Locale) ((hb_locale_t) 0)
Behdad Esfahbod13643932022-01-12 10:54:28 -070056
Ebrahim Byagowi19b8eb02019-06-11 01:33:30 +043057#endif
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -040058
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -070059/**
60 * SECTION:hb-common
Behdad Esfahbodcf5fa572018-10-27 04:50:38 -070061 * @title: hb-common
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -070062 * @short_description: Common data types
63 * @include: hb.h
64 *
65 * Common data types used across HarfBuzz are defined here.
66 **/
67
68
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050069/* hb_options_t */
70
Behdad Esfahbod4bc16ac2018-07-31 21:05:51 -070071hb_atomic_int_t _hb_options;
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050072
73void
Ebrahim Byagowie4120082018-12-17 21:31:01 +033074_hb_options_init ()
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050075{
76 hb_options_union_t u;
77 u.i = 0;
Bruce Mitchener8d1e4792018-10-18 22:18:42 +070078 u.opts.initialized = true;
Behdad Esfahbodbab02d32013-02-12 15:26:45 -050079
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040080 const char *c = getenv ("HB_OPTIONS");
81 if (c)
82 {
83 while (*c)
84 {
85 const char *p = strchr (c, ':');
86 if (!p)
Ebrahim Byagowia0b4ac42019-08-24 17:57:14 +043087 p = c + strlen (c);
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040088
89#define OPTION(name, symbol) \
GaryQianccf14482019-06-24 12:57:13 -070090 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 -040091
92 OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
Behdad Esfahbod38a7a8a2018-10-10 17:44:46 -040093
94#undef OPTION
95
96 c = *p ? p + 1 : p;
97 }
98
99 }
Behdad Esfahbodbab02d32013-02-12 15:26:45 -0500100
101 /* This is idempotent and threadsafe. */
Behdad Esfahbodf73c15c2022-08-03 12:54:03 -0600102 _hb_options = u.i;
Behdad Esfahbodbab02d32013-02-12 15:26:45 -0500103}
104
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -0400105
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400106/* hb_tag_t */
107
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400108/**
109 * hb_tag_from_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100110 * @str: (array length=len) (element-type uint8_t): String to convert
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200111 * @len: Length of @str, or -1 if it is `NULL`-terminated
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400112 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100113 * Converts a string into an #hb_tag_t. Valid tags
114 * are four characters. Shorter input strings will be
115 * padded with spaces. Longer input strings will be
116 * truncated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400117 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100118 * Return value: The #hb_tag_t corresponding to @str
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400119 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430120 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400121 **/
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100122hb_tag_t
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400123hb_tag_from_string (const char *str, int len)
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100124{
125 char tag[4];
126 unsigned int i;
127
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400128 if (!str || !len || !*str)
Behdad Esfahbod7ff74012011-04-11 13:27:30 -0400129 return HB_TAG_NONE;
130
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200131 if (len < 0 || len > 4)
132 len = 4;
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400133 for (i = 0; i < (unsigned) len && str[i]; i++)
134 tag[i] = str[i];
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100135 for (; i < 4; i++)
136 tag[i] = ' ';
137
Behdad Esfahbod8eaff982017-10-31 15:30:06 -0600138 return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
Behdad Esfahbod83f34672010-05-21 13:43:49 +0100139}
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -0400140
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400141/**
142 * hb_tag_to_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100143 * @tag: #hb_tag_t to convert
144 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400145 *
Behdad Esfahboda45a6302022-01-08 15:47:33 -0800146 * Converts an #hb_tag_t to a string and returns it in @buf.
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100147 * Strings will be four characters long.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400148 *
Sascha Brawer01c3a882015-06-01 13:22:01 +0200149 * Since: 0.9.5
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400150 **/
Behdad Esfahbode30ebd22012-09-06 22:09:06 -0400151void
152hb_tag_to_string (hb_tag_t tag, char *buf)
153{
154 buf[0] = (char) (uint8_t) (tag >> 24);
155 buf[1] = (char) (uint8_t) (tag >> 16);
156 buf[2] = (char) (uint8_t) (tag >> 8);
157 buf[3] = (char) (uint8_t) (tag >> 0);
158}
159
Behdad Esfahbodacdba3f2010-07-23 15:11:18 -0400160
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400161/* hb_direction_t */
162
Behdad Esfahbodc859cbf2022-06-16 13:55:12 -0600163static const char direction_strings[][4] = {
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400164 "ltr",
165 "rtl",
166 "ttb",
167 "btt"
168};
169
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400170/**
171 * hb_direction_from_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100172 * @str: (array length=len) (element-type uint8_t): String to convert
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200173 * @len: Length of @str, or -1 if it is `NULL`-terminated
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400174 *
Behdad Esfahboda45a6302022-01-08 15:47:33 -0800175 * Converts a string to an #hb_direction_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400176 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100177 * Matching is loose and applies only to the first letter. For
178 * examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430179 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100180 * Unmatched strings will return #HB_DIRECTION_INVALID.
Behdad Esfahboda45a6302022-01-08 15:47:33 -0800181 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100182 * Return value: The #hb_direction_t matching @str
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400183 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430184 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400185 **/
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400186hb_direction_t
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200187hb_direction_from_string (const char *str, int len)
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400188{
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200189 if (unlikely (!str || !len || !*str))
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400190 return HB_DIRECTION_INVALID;
191
192 /* Lets match loosely: just match the first letter, such that
193 * all of "ltr", "left-to-right", etc work!
194 */
195 char c = TOLOWER (str[0]);
196 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
197 if (c == direction_strings[i][0])
Behdad Esfahbod4bf90f62012-04-12 17:38:23 -0400198 return (hb_direction_t) (HB_DIRECTION_LTR + i);
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400199
200 return HB_DIRECTION_INVALID;
201}
202
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400203/**
204 * hb_direction_to_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100205 * @direction: The #hb_direction_t to convert
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400206 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100207 * Converts an #hb_direction_t to a string.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400208 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100209 * Return value: (transfer none): The string corresponding to @direction
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400210 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430211 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400212 **/
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400213const char *
214hb_direction_to_string (hb_direction_t direction)
215{
Behdad Esfahbod4bf90f62012-04-12 17:38:23 -0400216 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
217 < ARRAY_LENGTH (direction_strings)))
218 return direction_strings[direction - HB_DIRECTION_LTR];
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400219
220 return "invalid";
221}
222
223
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400224/* hb_language_t */
225
Behdad Esfahbod1bc1cb32012-06-16 15:21:55 -0400226struct hb_language_impl_t {
Behdad Esfahbod3cbdf702011-04-15 12:32:06 -0400227 const char s[1];
228};
229
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400230static const char canon_map[256] = {
231 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
232 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
233 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
234 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
David Corbett018ba462018-11-23 13:21:22 -0500235 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400236 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
237 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
238 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
239};
240
Behdad Esfahbodf3b170b2015-04-08 16:26:24 -0700241static bool
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400242lang_equal (hb_language_t v1,
243 const void *v2)
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400244{
Behdad Esfahbodc57d4542011-04-20 18:50:27 -0400245 const unsigned char *p1 = (const unsigned char *) v1;
246 const unsigned char *p2 = (const unsigned char *) v2;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400247
Chris Petersonaacca372017-04-17 23:25:24 -0700248 while (*p1 && *p1 == canon_map[*p2]) {
249 p1++;
250 p2++;
251 }
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400252
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400253 return *p1 == canon_map[*p2];
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400254}
255
256#if 0
257static unsigned int
258lang_hash (const void *key)
259{
260 const unsigned char *p = key;
261 unsigned int h = 0;
262 while (canon_map[*p])
263 {
264 h = (h << 5) - h + canon_map[*p];
265 p++;
266 }
267
268 return h;
269}
270#endif
271
272
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400273struct hb_language_item_t {
274
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400275 struct hb_language_item_t *next;
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400276 hb_language_t lang;
277
Ebrahim Byagowib2ebaa92018-12-16 22:38:10 +0330278 bool operator == (const char *s) const
279 { return lang_equal (lang, s); }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400280
Behdad Esfahbodbb48bf52021-07-08 10:53:45 -0600281 hb_language_item_t & operator = (const char *s)
282 {
283 /* We can't call strdup(), because we allow custom allocators. */
Sebastian Rasmussen92e2c4b2017-05-29 12:53:30 -0500284 size_t len = strlen(s) + 1;
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600285 lang = (hb_language_t) hb_malloc(len);
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400286 if (likely (lang))
287 {
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900288 hb_memcpy((unsigned char *) lang, s, len);
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400289 for (unsigned char *p = (unsigned char *) lang; *p; p++)
290 *p = canon_map[*p];
291 }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400292
293 return *this;
294 }
295
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600296 void fini () { hb_free ((void *) lang); }
Behdad Esfahbodb45f32e2011-05-05 15:00:43 -0400297};
298
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400299
Behdad Esfahbodbb48bf52021-07-08 10:53:45 -0600300/* Thread-safe lockfree language list */
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400301
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700302static hb_atomic_ptr_t <hb_language_item_t> langs;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400303
Behdad Esfahboded116322021-09-14 07:09:54 -0400304static inline void
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330305free_langs ()
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400306{
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700307retry:
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500308 hb_language_item_t *first_lang = langs;
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700309 if (unlikely (!langs.cmpexch (first_lang, nullptr)))
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700310 goto retry;
311
312 while (first_lang) {
313 hb_language_item_t *next = first_lang->next;
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400314 first_lang->fini ();
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600315 hb_free (first_lang);
Behdad Esfahbod5aa2c6e2018-03-28 15:33:51 -0700316 first_lang = next;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400317 }
318}
319
320static hb_language_item_t *
321lang_find_or_insert (const char *key)
322{
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400323retry:
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500324 hb_language_item_t *first_lang = langs;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400325
326 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
327 if (*lang == key)
328 return lang;
329
330 /* Not found; allocate one. */
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600331 hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400332 if (unlikely (!lang))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200333 return nullptr;
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400334 lang->next = first_lang;
335 *lang = key;
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400336 if (unlikely (!lang->lang))
337 {
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600338 hb_free (lang);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200339 return nullptr;
Behdad Esfahbod7dba3062017-06-01 11:44:42 -0400340 }
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400341
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700342 if (unlikely (!langs.cmpexch (first_lang, lang)))
343 {
Behdad Esfahboda60ba792018-05-01 19:01:25 -0400344 lang->fini ();
Behdad Esfahbod2337f0d2021-07-08 10:58:50 -0600345 hb_free (lang);
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400346 goto retry;
347 }
348
Behdad Esfahbod04aed572012-06-05 18:30:19 -0400349 if (!first_lang)
Behdad Esfahboded116322021-09-14 07:09:54 -0400350 hb_atexit (free_langs); /* First person registers atexit() callback. */
Behdad Esfahbod093171c2012-06-05 18:00:45 -0400351
352 return lang;
353}
354
Behdad Esfahbodb8d61832011-05-05 15:14:04 -0400355
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400356/**
357 * hb_language_from_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400358 * @str: (array length=len) (element-type uint8_t): a string representing
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200359 * a BCP 47 language tag
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200360 * @len: length of the @str, or -1 if it is `NULL`-terminated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400361 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200362 * Converts @str representing a BCP 47 language tag to the corresponding
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400363 * #hb_language_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400364 *
Khaled Hosny04f89e82015-04-10 17:49:01 +0200365 * Return value: (transfer none):
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200366 * The #hb_language_t corresponding to the BCP 47 language tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400367 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430368 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400369 **/
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400370hb_language_t
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200371hb_language_from_string (const char *str, int len)
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400372{
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200373 if (!str || !len || !*str)
Behdad Esfahbod1a64f6e2011-05-13 22:55:32 -0400374 return HB_LANGUAGE_INVALID;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400375
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200376 hb_language_item_t *item = nullptr;
Behdad Esfahbod48360ec2013-09-26 16:48:42 -0400377 if (len >= 0)
378 {
Behdad Esfahboddac86022014-06-03 17:57:00 -0400379 /* NUL-terminate it. */
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100380 char strbuf[64];
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700381 len = hb_min (len, (int) sizeof (strbuf) - 1);
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900382 hb_memcpy (strbuf, str, len);
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200383 strbuf[len] = '\0';
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100384 item = lang_find_or_insert (strbuf);
Behdad Esfahbod4c9fe882011-08-26 09:18:53 +0200385 }
Behdad Esfahbodf3159ba2015-09-29 14:34:56 +0100386 else
387 item = lang_find_or_insert (str);
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400388
Behdad Esfahbod1a64f6e2011-05-13 22:55:32 -0400389 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400390}
391
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400392/**
393 * hb_language_to_string:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100394 * @language: The #hb_language_t to convert
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400395 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100396 * Converts an #hb_language_t to a string.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400397 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400398 * Return value: (transfer none):
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200399 * A `NULL`-terminated string representing the @language. Must not be freed by
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400400 * the caller.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400401 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430402 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400403 **/
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400404const char *
405hb_language_to_string (hb_language_t language)
406{
Ebrahim Byagowie6909ee2019-09-18 22:12:25 +0430407 if (unlikely (!language)) return nullptr;
Ebrahim Byagowid8af9ee2019-09-18 00:47:55 +0430408
Behdad Esfahbod3cbdf702011-04-15 12:32:06 -0400409 return language->s;
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400410}
411
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400412/**
413 * hb_language_get_default:
414 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100415 * Fetch the default language from current locale.
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430416 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100417 * <note>Note that the first time this function is called, it calls
Behdad Esfahbodba0f0f12018-09-30 03:49:52 -0400418 * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying
419 * setlocale function is, in many implementations, NOT threadsafe. To avoid
420 * problems, call this function once before multiple threads can call it.
421 * This function is only used from hb_buffer_guess_segment_properties() by
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100422 * HarfBuzz itself.</note>
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400423 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100424 * Return value: (transfer none): The default language of the locale as
425 * an #hb_language_t
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400426 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430427 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400428 **/
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400429hb_language_t
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330430hb_language_get_default ()
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400431{
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700432 static hb_atomic_ptr_t <hb_language_t> default_language;
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400433
Behdad Esfahbodf6fc5572018-11-05 13:23:54 -0500434 hb_language_t language = default_language;
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700435 if (unlikely (language == HB_LANGUAGE_INVALID))
436 {
Behdad Esfahbodb97e4f72022-01-15 17:47:51 -0700437 language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1);
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700438 (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400439 }
440
Behdad Esfahbod1f738092018-08-09 00:22:37 -0700441 return language;
Behdad Esfahbod34fb5522011-05-06 00:04:28 -0400442}
443
Behdad Esfahbodf7f6d272022-07-17 22:15:42 -0600444/**
445 * hb_language_matches:
446 * @language: The #hb_language_t to work on
447 * @specific: Another #hb_language_t
448 *
449 * Check whether a second language tag is the same or a more
450 * specific version of the provided language tag. For example,
451 * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR".
452 *
453 * Return value: `true` if languages match, `false` otherwise.
454 *
Khaled Hosny40b21ed2022-07-23 16:45:32 +0200455 * Since: 5.0.0
Behdad Esfahbodf7f6d272022-07-17 22:15:42 -0600456 **/
457hb_bool_t
458hb_language_matches (hb_language_t language,
459 hb_language_t specific)
460{
461 if (language == specific) return true;
462 if (!language || !specific) return false;
463
464 const char *l = language->s;
465 const char *s = specific->s;
466 unsigned ll = strlen (l);
467 unsigned sl = strlen (s);
468
469 if (ll > sl)
470 return false;
471
472 return strncmp (l, s, ll) == 0 &&
473 (s[ll] == '\0' || s[ll] == '-');
474}
475
Behdad Esfahbod8e4bb3c2011-04-11 17:55:58 -0400476
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400477/* hb_script_t */
478
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400479/**
480 * hb_script_from_iso15924_tag:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200481 * @tag: an #hb_tag_t representing an ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400482 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200483 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400484 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430485 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200486 * An #hb_script_t corresponding to the ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400487 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430488 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400489 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400490hb_script_t
491hb_script_from_iso15924_tag (hb_tag_t tag)
492{
Behdad Esfahbodf144a8e2011-04-20 02:54:42 -0400493 if (unlikely (tag == HB_TAG_NONE))
494 return HB_SCRIPT_INVALID;
495
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400496 /* Be lenient, adjust case (one capital letter followed by three small letters) */
Behdad Esfahbod76271002014-07-11 14:54:42 -0400497 tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400498
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400499 switch (tag) {
Behdad Esfahbodd02985e2011-05-02 12:35:14 -0400500
501 /* These graduated from the 'Q' private-area codes, but
502 * the old code is still aliased by Unicode, and the Qaai
503 * one in use by ICU. */
504 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
505 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
506
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +0430507 /* Script variants from https://unicode.org/iso15924/ */
David Corbett3bd43bd2020-11-16 21:55:02 -0500508 case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400509 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
David Corbett3bd43bd2020-11-16 21:55:02 -0500510 case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;
511 case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;
512 case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;
513 case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400514 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
515 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
516 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
517 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
518 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
519 }
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400520
521 /* If it looks right, just use the tag as a script */
Behdad Esfahbod76271002014-07-11 14:54:42 -0400522 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400523 return (hb_script_t) tag;
524
525 /* Otherwise, return unknown */
526 return HB_SCRIPT_UNKNOWN;
527}
528
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400529/**
530 * hb_script_from_string:
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400531 * @str: (array length=len) (element-type uint8_t): a string representing an
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200532 * ISO 15924 tag.
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200533 * @len: length of the @str, or -1 if it is `NULL`-terminated.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400534 *
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200535 * Converts a string @str representing an ISO 15924 script tag to a
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400536 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
537 * hb_script_from_iso15924_tag().
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400538 *
Ebrahim Byagowi70d36542018-03-30 05:00:28 +0430539 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200540 * An #hb_script_t corresponding to the ISO 15924 tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400541 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430542 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400543 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400544hb_script_t
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400545hb_script_from_string (const char *str, int len)
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400546{
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400547 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400548}
549
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400550/**
551 * hb_script_to_iso15924_tag:
David Corbett5daeff32019-04-17 09:11:44 -0400552 * @script: an #hb_script_t to convert.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400553 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100554 * Converts an #hb_script_t to a corresponding ISO 15924 script tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400555 *
Khaled Hosny8ab797c2015-12-29 17:42:16 +0400556 * Return value:
Evgeniy Reiznerb618e0a2019-12-15 16:26:50 +0200557 * An #hb_tag_t representing an ISO 15924 script tag.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400558 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430559 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400560 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400561hb_tag_t
562hb_script_to_iso15924_tag (hb_script_t script)
563{
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400564 return (hb_tag_t) script;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400565}
566
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400567/**
568 * hb_script_get_horizontal_direction:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100569 * @script: The #hb_script_t to query
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400570 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100571 * Fetches the #hb_direction_t of a script when it is
572 * set horizontally. All right-to-left scripts will return
573 * #HB_DIRECTION_RTL. All left-to-right scripts will return
Khaled Hosny8586f152020-12-24 22:23:47 +0200574 * #HB_DIRECTION_LTR. Scripts that can be written either
575 * horizontally or vertically will return #HB_DIRECTION_INVALID.
576 * Unknown scripts will return #HB_DIRECTION_LTR.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400577 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100578 * Return value: The horizontal #hb_direction_t of @script
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400579 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430580 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400581 **/
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400582hb_direction_t
583hb_script_get_horizontal_direction (hb_script_t script)
584{
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +0430585 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400586 switch ((hb_tag_t) script)
587 {
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500588 /* Unicode-1.1 additions */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400589 case HB_SCRIPT_ARABIC:
590 case HB_SCRIPT_HEBREW:
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500591
592 /* Unicode-3.0 additions */
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400593 case HB_SCRIPT_SYRIAC:
594 case HB_SCRIPT_THAANA:
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400595
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400596 /* Unicode-4.0 additions */
597 case HB_SCRIPT_CYPRIOT:
598
Behdad Esfahbod50e810c2012-03-07 12:49:08 -0500599 /* Unicode-4.1 additions */
600 case HB_SCRIPT_KHAROSHTHI:
601
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400602 /* Unicode-5.0 additions */
603 case HB_SCRIPT_PHOENICIAN:
604 case HB_SCRIPT_NKO:
605
Behdad Esfahbod50e810c2012-03-07 12:49:08 -0500606 /* Unicode-5.1 additions */
607 case HB_SCRIPT_LYDIAN:
608
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400609 /* Unicode-5.2 additions */
610 case HB_SCRIPT_AVESTAN:
611 case HB_SCRIPT_IMPERIAL_ARAMAIC:
612 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
613 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
614 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
615 case HB_SCRIPT_OLD_TURKIC:
616 case HB_SCRIPT_SAMARITAN:
617
618 /* Unicode-6.0 additions */
619 case HB_SCRIPT_MANDAIC:
620
Behdad Esfahbodfa2673c2012-03-07 15:52:02 -0500621 /* Unicode-6.1 additions */
622 case HB_SCRIPT_MEROITIC_CURSIVE:
623 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
624
Behdad Esfahboda4a78992014-04-28 15:06:42 -0700625 /* Unicode-7.0 additions */
626 case HB_SCRIPT_MANICHAEAN:
627 case HB_SCRIPT_MENDE_KIKAKUI:
628 case HB_SCRIPT_NABATAEAN:
629 case HB_SCRIPT_OLD_NORTH_ARABIAN:
630 case HB_SCRIPT_PALMYRENE:
631 case HB_SCRIPT_PSALTER_PAHLAVI:
632
Behdad Esfahbod64a27262015-07-15 01:36:39 +0100633 /* Unicode-8.0 additions */
Cosimo Lupoc8f2a4f2018-01-18 22:49:40 +0100634 case HB_SCRIPT_HATRAN:
Behdad Esfahbod64a27262015-07-15 01:36:39 +0100635
Behdad Esfahbod691086f2016-05-06 12:08:18 +0100636 /* Unicode-9.0 additions */
637 case HB_SCRIPT_ADLAM:
638
Behdad Esfahbod060e6b42018-06-05 17:31:46 -0700639 /* Unicode-11.0 additions */
640 case HB_SCRIPT_HANIFI_ROHINGYA:
641 case HB_SCRIPT_OLD_SOGDIAN:
642 case HB_SCRIPT_SOGDIAN:
643
David Corbett665483c2020-04-01 17:28:12 -0400644 /* Unicode-12.0 additions */
645 case HB_SCRIPT_ELYMAIC:
646
David Corbettfd748fa2020-03-15 15:59:31 -0400647 /* Unicode-13.0 additions */
648 case HB_SCRIPT_CHORASMIAN:
649 case HB_SCRIPT_YEZIDI:
650
David Corbett7b05eec2021-09-14 17:51:11 -0400651 /* Unicode-14.0 additions */
652 case HB_SCRIPT_OLD_UYGHUR:
653
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400654 return HB_DIRECTION_RTL;
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700655
656
657 /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
David Corbettd8d1e7d2018-09-17 11:09:51 -0400658 case HB_SCRIPT_OLD_HUNGARIAN:
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700659 case HB_SCRIPT_OLD_ITALIC:
David Corbett46d8f0d2018-07-06 15:47:03 -0400660 case HB_SCRIPT_RUNIC:
Behdad Esfahbodf673cfb2018-05-07 13:58:32 -0700661
662 return HB_DIRECTION_INVALID;
Behdad Esfahbod62879ee2011-04-18 23:40:21 -0400663 }
664
665 return HB_DIRECTION_LTR;
Behdad Esfahbod00bec2c2011-04-15 19:16:54 -0400666}
667
668
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400669/* hb_version */
670
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -0700671
672/**
673 * SECTION:hb-version
Behdad Esfahbodcf5fa572018-10-27 04:50:38 -0700674 * @title: hb-version
Behdad Esfahbod00cf4e52018-10-27 04:07:33 -0700675 * @short_description: Information about the version of HarfBuzz in use
676 * @include: hb.h
677 *
678 * These functions and macros allow accessing version of the HarfBuzz
679 * library used at compile- as well as run-time, and to direct code
680 * conditionally based on those versions, again, at compile- or run-time.
681 **/
682
683
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400684/**
685 * hb_version:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100686 * @major: (out): Library major version component
687 * @minor: (out): Library minor version component
688 * @micro: (out): Library micro version component
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400689 *
690 * Returns library version as three integer components.
691 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430692 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400693 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400694void
695hb_version (unsigned int *major,
696 unsigned int *minor,
697 unsigned int *micro)
698{
699 *major = HB_VERSION_MAJOR;
700 *minor = HB_VERSION_MINOR;
701 *micro = HB_VERSION_MICRO;
702}
703
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400704/**
705 * hb_version_string:
706 *
707 * Returns library version as a string with three components.
708 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100709 * Return value: Library version string
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400710 *
Behdad Esfahbodb8811422015-09-03 15:53:22 +0430711 * Since: 0.9.2
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400712 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400713const char *
Ebrahim Byagowie4120082018-12-17 21:31:01 +0330714hb_version_string ()
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400715{
716 return HB_VERSION_STRING;
717}
718
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400719/**
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400720 * hb_version_atleast:
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100721 * @major: Library major version component
722 * @minor: Library minor version component
723 * @micro: Library micro version component
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400724 *
Nathan Willis4cdb12a2019-04-21 20:31:09 +0100725 * Tests the library version against a minimum value,
726 * as three integer components.
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400727 *
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200728 * Return value: `true` if the library is equal to or greater than
729 * the test value, `false` otherwise
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400730 *
Sascha Brawer01c3a882015-06-01 13:22:01 +0200731 * Since: 0.9.30
Behdad Esfahbod70303cf2013-09-06 17:35:57 -0400732 **/
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400733hb_bool_t
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400734hb_version_atleast (unsigned int major,
735 unsigned int minor,
736 unsigned int micro)
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400737{
Behdad Esfahbod2b051c62014-06-20 14:09:57 -0400738 return HB_VERSION_ATLEAST (major, minor, micro);
Behdad Esfahbodc78f4482011-05-05 21:31:04 -0400739}
Behdad Esfahbod72364102017-01-20 20:16:53 -0800740
741
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800742
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -0800743/* hb_feature_t and hb_variation_t */
Behdad Esfahbod72364102017-01-20 20:16:53 -0800744
745static bool
746parse_space (const char **pp, const char *end)
747{
748 while (*pp < end && ISSPACE (**pp))
749 (*pp)++;
750 return true;
751}
752
753static bool
754parse_char (const char **pp, const char *end, char c)
755{
756 parse_space (pp, end);
757
758 if (*pp == end || **pp != c)
759 return false;
760
761 (*pp)++;
762 return true;
763}
764
765static bool
766parse_uint (const char **pp, const char *end, unsigned int *pv)
767{
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430768 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
769 * such that -1 turns into "big number"... */
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430770 int v;
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430771 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800772
773 *pv = v;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800774 return true;
775}
776
777static bool
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430778parse_uint32 (const char **pp, const char *end, uint32_t *pv)
779{
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430780 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
781 * such that -1 turns into "big number"... */
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430782 int v;
Ebrahim Byagowib5e68052019-09-03 15:23:40 +0430783 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430784
785 *pv = v;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800786 return true;
787}
788
789static bool
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430790parse_bool (const char **pp, const char *end, uint32_t *pv)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800791{
792 parse_space (pp, end);
793
794 const char *p = *pp;
795 while (*pp < end && ISALPHA(**pp))
796 (*pp)++;
797
798 /* CSS allows on/off as aliases 1/0. */
David Corbett45adc182019-02-18 22:30:40 -0500799 if (*pp - p == 2
800 && TOLOWER (p[0]) == 'o'
801 && TOLOWER (p[1]) == 'n')
Behdad Esfahbod72364102017-01-20 20:16:53 -0800802 *pv = 1;
David Corbett45adc182019-02-18 22:30:40 -0500803 else if (*pp - p == 3
804 && TOLOWER (p[0]) == 'o'
805 && TOLOWER (p[1]) == 'f'
806 && TOLOWER (p[2]) == 'f')
Behdad Esfahbod72364102017-01-20 20:16:53 -0800807 *pv = 0;
808 else
809 return false;
810
811 return true;
812}
813
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800814/* hb_feature_t */
815
Behdad Esfahbod72364102017-01-20 20:16:53 -0800816static bool
817parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
818{
819 if (parse_char (pp, end, '-'))
820 feature->value = 0;
821 else {
822 parse_char (pp, end, '+');
823 feature->value = 1;
824 }
825
826 return true;
827}
828
829static bool
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800830parse_tag (const char **pp, const char *end, hb_tag_t *tag)
Behdad Esfahbod72364102017-01-20 20:16:53 -0800831{
832 parse_space (pp, end);
833
834 char quote = 0;
835
836 if (*pp < end && (**pp == '\'' || **pp == '"'))
837 {
838 quote = **pp;
839 (*pp)++;
840 }
841
842 const char *p = *pp;
Martin Hosken39607dc2018-08-09 15:16:32 +0700843 while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
Behdad Esfahbod72364102017-01-20 20:16:53 -0800844 (*pp)++;
845
846 if (p == *pp || *pp - p > 4)
847 return false;
848
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800849 *tag = hb_tag_from_string (p, *pp - p);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800850
851 if (quote)
852 {
853 /* CSS expects exactly four bytes. And we only allow quotations for
854 * CSS compatibility. So, enforce the length. */
855 if (*pp - p != 4)
856 return false;
857 if (*pp == end || **pp != quote)
858 return false;
859 (*pp)++;
860 }
861
862 return true;
863}
864
865static bool
866parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
867{
868 parse_space (pp, end);
869
870 bool has_start;
871
Behdad Esfahbodbecd84a2018-09-11 01:26:18 +0200872 feature->start = HB_FEATURE_GLOBAL_START;
873 feature->end = HB_FEATURE_GLOBAL_END;
Behdad Esfahbod72364102017-01-20 20:16:53 -0800874
875 if (!parse_char (pp, end, '['))
876 return true;
877
878 has_start = parse_uint (pp, end, &feature->start);
879
Behdad Esfahbod88694362018-10-23 03:07:48 -0700880 if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
Behdad Esfahbod72364102017-01-20 20:16:53 -0800881 parse_uint (pp, end, &feature->end);
882 } else {
883 if (has_start)
884 feature->end = feature->start + 1;
885 }
886
887 return parse_char (pp, end, ']');
888}
889
890static bool
891parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
892{
893 bool had_equal = parse_char (pp, end, '=');
Ebrahim Byagowi3b0e47c2017-06-19 14:47:09 +0430894 bool had_value = parse_uint32 (pp, end, &feature->value) ||
Ebrahim Byagowia0b4ac42019-08-24 17:57:14 +0430895 parse_bool (pp, end, &feature->value);
Behdad Esfahbod72364102017-01-20 20:16:53 -0800896 /* CSS doesn't use equal-sign between tag and value.
897 * If there was an equal-sign, then there *must* be a value.
Bruce Mitchener90218fa2018-01-31 20:44:45 +0700898 * A value without an equal-sign is ok, but not required. */
Behdad Esfahbod72364102017-01-20 20:16:53 -0800899 return !had_equal || had_value;
900}
901
Behdad Esfahbod72364102017-01-20 20:16:53 -0800902static bool
903parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
904{
905 return parse_feature_value_prefix (pp, end, feature) &&
Behdad Esfahbodb3c07142017-01-20 20:30:03 -0800906 parse_tag (pp, end, &feature->tag) &&
Behdad Esfahbod72364102017-01-20 20:16:53 -0800907 parse_feature_indices (pp, end, feature) &&
908 parse_feature_value_postfix (pp, end, feature) &&
909 parse_space (pp, end) &&
910 *pp == end;
911}
912
913/**
914 * hb_feature_from_string:
915 * @str: (array length=len) (element-type uint8_t): a string to parse
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200916 * @len: length of @str, or -1 if string is `NULL` terminated
Behdad Esfahbod72364102017-01-20 20:16:53 -0800917 * @feature: (out): the #hb_feature_t to initialize with the parsed values
918 *
919 * Parses a string into a #hb_feature_t.
920 *
Khaled Hosny6bd4c082019-02-19 02:23:58 +0200921 * The format for specifying feature strings follows. All valid CSS
David Corbett45adc182019-02-18 22:30:40 -0500922 * font-feature-settings values other than 'normal' and the global values are
923 * also accepted, though not documented below. CSS string escapes are not
924 * supported.
Khaled Hosny6bd4c082019-02-19 02:23:58 +0200925 *
926 * The range indices refer to the positions between Unicode characters. The
927 * position before the first character is always 0.
928 *
929 * The format is Python-esque. Here is how it all works:
930 *
931 * <informaltable pgwide='1' align='left' frame='none'>
932 * <tgroup cols='5'>
933 * <thead>
934 * <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
935 * </thead>
936 * <tbody>
937 * <row><entry>Setting value:</entry></row>
938 * <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
939 * <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
940 * <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
941 * <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
942 * <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
943 * <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>
944 * <row><entry>Setting index:</entry></row>
945 * <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
946 * <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
947 * <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>
948 * <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>
949 * <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>
950 * <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
951 * <row><entry>Mixing it all:</entry></row>
952 * <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>
953 * </tbody>
954 * </tgroup>
955 * </informaltable>
Behdad Esfahbod72364102017-01-20 20:16:53 -0800956 *
957 * Return value:
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200958 * `true` if @str is successfully parsed, `false` otherwise
Behdad Esfahbod72364102017-01-20 20:16:53 -0800959 *
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)
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900979 hb_memset (feature, 0, sizeof (*feature));
Behdad Esfahbod72364102017-01-20 20:16:53 -0800980 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 *
Khaled Hosny98e90cc2022-06-30 08:43:57 +0200989 * Converts a #hb_feature_t into a `NULL`-terminated string in the format
Behdad Esfahbod72364102017-01-20 20:16:53 -0800990 * 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--;
Evgeniy Reiznerb79ceac2019-12-15 16:50:01 +02001009 if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
Behdad Esfahbod72364102017-01-20 20:16:53 -08001010 {
1011 s[len++] = '[';
1012 if (feature->start)
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001013 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
Behdad Esfahbod72364102017-01-20 20:16:53 -08001014 if (feature->end != feature->start + 1) {
1015 s[len++] = ':';
Evgeniy Reiznerb79ceac2019-12-15 16:50:01 +02001016 if (feature->end != HB_FEATURE_GLOBAL_END)
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001017 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
Behdad Esfahbod72364102017-01-20 20:16:53 -08001018 }
1019 s[len++] = ']';
1020 }
1021 if (feature->value > 1)
1022 {
1023 s[len++] = '=';
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001024 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
Behdad Esfahbod72364102017-01-20 20:16:53 -08001025 }
1026 assert (len < ARRAY_LENGTH (s));
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001027 len = hb_min (len, size - 1);
Seigo Nonakac62d6f42023-03-01 19:52:57 +09001028 hb_memcpy (buf, s, len);
Behdad Esfahbod72364102017-01-20 20:16:53 -08001029 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. */
Ebrahim Byagowi57f88e12019-09-04 01:20:50 +04301038 double v;
1039 if (unlikely (!hb_parse_double (pp, end, &v))) return false;
1040
1041 variation->value = v;
1042 return true;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001043}
1044
1045static bool
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001046parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001047{
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001048 return parse_tag (pp, end, &variation->tag) &&
1049 parse_variation_value (pp, end, variation) &&
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001050 parse_space (pp, end) &&
1051 *pp == end;
1052}
1053
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001054/**
1055 * hb_variation_from_string:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001056 * @str: (array length=len) (element-type uint8_t): a string to parse
Khaled Hosny98e90cc2022-06-30 08:43:57 +02001057 * @len: length of @str, or -1 if string is `NULL` terminated
Khaled Hosny9b602e82020-12-30 23:28:54 +02001058 * @variation: (out): the #hb_variation_t to initialize with the parsed values
1059 *
1060 * Parses a string into a #hb_variation_t.
1061 *
1062 * The format for specifying variation settings follows. All valid CSS
1063 * font-variation-settings values other than 'normal' and 'inherited' are also
1064 * accepted, though, not documented below.
1065 *
1066 * The format is a tag, optionally followed by an equals sign, followed by a
1067 * number. For example `wght=500`, or `slnt=-7.5`.
1068 *
1069 * Return value:
Khaled Hosny98e90cc2022-06-30 08:43:57 +02001070 * `true` if @str is successfully parsed, `false` otherwise
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001071 *
1072 * Since: 1.4.2
1073 */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001074hb_bool_t
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001075hb_variation_from_string (const char *str, int len,
1076 hb_variation_t *variation)
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001077{
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001078 hb_variation_t var;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001079
1080 if (len < 0)
1081 len = strlen (str);
1082
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001083 if (likely (parse_one_variation (&str, str + len, &var)))
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001084 {
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001085 if (variation)
1086 *variation = var;
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001087 return true;
1088 }
1089
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001090 if (variation)
Seigo Nonakac62d6f42023-03-01 19:52:57 +09001091 hb_memset (variation, 0, sizeof (*variation));
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001092 return false;
1093}
1094
Behdad Esfahbod13643932022-01-12 10:54:28 -07001095#ifndef HB_NO_SETLOCALE
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001096
1097static inline void free_static_C_locale ();
1098
Behdad Esfahbod13643932022-01-12 10:54:28 -07001099static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,
Behdad Esfahbod68937232022-02-11 13:16:25 -06001100 hb_C_locale_lazy_loader_t>
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001101{
Behdad Esfahbod13643932022-01-12 10:54:28 -07001102 static hb_locale_t create ()
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001103 {
Behdad Esfahbod13643932022-01-12 10:54:28 -07001104 hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001105 if (!l)
1106 return l;
1107
1108 hb_atexit (free_static_C_locale);
1109
1110 return l;
1111 }
Behdad Esfahbod13643932022-01-12 10:54:28 -07001112 static void destroy (hb_locale_t l)
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001113 {
1114 freelocale (l);
1115 }
Behdad Esfahbod13643932022-01-12 10:54:28 -07001116 static hb_locale_t get_null ()
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001117 {
Behdad Esfahbod13643932022-01-12 10:54:28 -07001118 return (hb_locale_t) 0;
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001119 }
1120} static_C_locale;
1121
1122static inline
1123void free_static_C_locale ()
1124{
1125 static_C_locale.free_instance ();
1126}
1127
Behdad Esfahbod13643932022-01-12 10:54:28 -07001128static hb_locale_t
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001129get_C_locale ()
1130{
1131 return static_C_locale.get_unconst ();
1132}
1133
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001134#endif
1135
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001136/**
1137 * hb_variation_to_string:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001138 * @variation: an #hb_variation_t to convert
Seigo Nonakac62d6f42023-03-01 19:52:57 +09001139 * @buf: (array length=size) (out caller-allocates): output string
Khaled Hosny9b602e82020-12-30 23:28:54 +02001140 * @size: the allocated size of @buf
1141 *
Khaled Hosny98e90cc2022-06-30 08:43:57 +02001142 * Converts an #hb_variation_t into a `NULL`-terminated string in the format
Khaled Hosny9b602e82020-12-30 23:28:54 +02001143 * understood by hb_variation_from_string(). The client in responsible for
1144 * allocating big enough size for @buf, 128 bytes is more than enough.
Behdad Esfahbodd2f249e2017-01-22 17:42:33 -08001145 *
1146 * Since: 1.4.2
1147 */
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001148void
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001149hb_variation_to_string (hb_variation_t *variation,
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001150 char *buf, unsigned int size)
1151{
1152 if (unlikely (!size)) return;
1153
1154 char s[128];
1155 unsigned int len = 0;
Behdad Esfahbodbb1e1922017-01-21 17:41:37 -08001156 hb_tag_to_string (variation->tag, s + len);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001157 len += 4;
1158 while (len && s[len - 1] == ' ')
1159 len--;
1160 s[len++] = '=';
Behdad Esfahboda45a6302022-01-08 15:47:33 -08001161
Behdad Esfahbod13643932022-01-12 10:54:28 -07001162 hb_locale_t oldlocale HB_UNUSED;
Behdad Esfahbodb97e4f72022-01-15 17:47:51 -07001163 oldlocale = hb_uselocale (get_C_locale ());
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001164 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
Behdad Esfahbodb97e4f72022-01-15 17:47:51 -07001165 (void) hb_uselocale (oldlocale);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001166
1167 assert (len < ARRAY_LENGTH (s));
Behdad Esfahbod41248cc2019-05-07 20:54:31 -07001168 len = hb_min (len, size - 1);
Seigo Nonakac62d6f42023-03-01 19:52:57 +09001169 hb_memcpy (buf, s, len);
Behdad Esfahbodb3c07142017-01-20 20:30:03 -08001170 buf[len] = '\0';
1171}
Behdad Esfahbode22a48a2018-07-23 13:24:26 -07001172
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001173/**
1174 * hb_color_get_alpha:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001175 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001176 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001177 * Fetches the alpha channel of the given @color.
1178 *
1179 * Return value: Alpha channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001180 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001181 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001182 */
1183uint8_t
1184(hb_color_get_alpha) (hb_color_t color)
1185{
1186 return hb_color_get_alpha (color);
1187}
1188
1189/**
1190 * hb_color_get_red:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001191 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001192 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001193 * Fetches the red channel of the given @color.
1194 *
1195 * Return value: Red channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001196 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001197 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001198 */
1199uint8_t
1200(hb_color_get_red) (hb_color_t color)
1201{
1202 return hb_color_get_red (color);
1203}
1204
1205/**
1206 * hb_color_get_green:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001207 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001208 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001209 * Fetches the green channel of the given @color.
1210 *
1211 * Return value: Green channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001212 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001213 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001214 */
1215uint8_t
1216(hb_color_get_green) (hb_color_t color)
1217{
1218 return hb_color_get_green (color);
1219}
1220
1221/**
1222 * hb_color_get_blue:
Khaled Hosny9b602e82020-12-30 23:28:54 +02001223 * @color: an #hb_color_t we are interested in its channels.
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001224 *
Khaled Hosny4bfa0b32020-12-31 16:30:05 +02001225 * Fetches the blue channel of the given @color.
1226 *
1227 * Return value: Blue channel value
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001228 *
Behdad Esfahbod1da08912019-05-24 15:41:34 -04001229 * Since: 2.1.0
Ebrahim Byagowi9542bdd2019-04-29 14:52:28 -07001230 */
1231uint8_t
1232(hb_color_get_blue) (hb_color_t color)
1233{
1234 return hb_color_get_blue (color);
1235}
1236
1237
Behdad Esfahbode22a48a2018-07-23 13:24:26 -07001238/* If there is no visibility control, then hb-static.cc will NOT
1239 * define anything. Instead, we get it to define one set in here
1240 * only, so only libharfbuzz.so defines them, not other libs. */
1241#ifdef HB_NO_VISIBILITY
1242#undef HB_NO_VISIBILITY
1243#include "hb-static.cc"
1244#define HB_NO_VISIBILITY 1
1245#endif