1/*
2 * Copyright © 2009,2010 Red Hat, Inc.
3 * Copyright © 2011,2012 Google, Inc.
4 *
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
26 * Google Author(s): Behdad Esfahbod
27 */
28
29#include "hb.hh"
30#include "hb-machinery.hh"
31
32
33/**
34 * SECTION:hb-common
35 * @title: hb-common
36 * @short_description: Common data types
37 * @include: hb.h
38 *
39 * Common data types used across HarfBuzz are defined here.
40 **/
41
42
43/* hb_options_t */
44
45hb_atomic_int_t _hb_options;
46
47void
48_hb_options_init ()
49{
50 hb_options_union_t u;
51 u.i = 0;
52 u.opts.initialized = true;
53
54 const char *c = getenv ("HB_OPTIONS");
55 if (c)
56 {
57 while (*c)
58 {
59 const char *p = strchr (c, ':');
60 if (!p)
61 p = c + strlen (c);
62
63#define OPTION(name, symbol) \
64 if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0)
65
66 OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
67
68#undef OPTION
69
70 c = *p ? p + 1 : p;
71 }
72
73 }
74
75 /* This is idempotent and threadsafe. */
76 _hb_options = u.i;
77}
78
79
80/* hb_tag_t */
81
82/**
83 * hb_tag_from_string:
84 * @str: (array length=len) (element-type uint8_t): String to convert
85 * @len: Length of @str, or -1 if it is `NULL`-terminated
86 *
87 * Converts a string into an #hb_tag_t. Valid tags
88 * are four characters. Shorter input strings will be
89 * padded with spaces. Longer input strings will be
90 * truncated.
91 *
92 * Return value: The #hb_tag_t corresponding to @str
93 *
94 * Since: 0.9.2
95 **/
96hb_tag_t
97hb_tag_from_string (const char *str, int len)
98{
99 char tag[4];
100 unsigned int i;
101
102 if (!str || !len || !*str)
103 return HB_TAG_NONE;
104
105 if (len < 0 || len > 4)
106 len = 4;
107 for (i = 0; i < (unsigned) len && str[i]; i++)
108 tag[i] = str[i];
109 for (; i < 4; i++)
110 tag[i] = ' ';
111
112 return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
113}
114
115/**
116 * hb_tag_to_string:
117 * @tag: #hb_tag_t to convert
118 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string
119 *
120 * Converts an #hb_tag_t to a string and returns it in @buf.
121 * Strings will be four characters long.
122 *
123 * Since: 0.9.5
124 **/
125void
126hb_tag_to_string (hb_tag_t tag, char *buf)
127{
128 buf[0] = (char) (uint8_t) (tag >> 24);
129 buf[1] = (char) (uint8_t) (tag >> 16);
130 buf[2] = (char) (uint8_t) (tag >> 8);
131 buf[3] = (char) (uint8_t) (tag >> 0);
132}
133
134
135/* hb_direction_t */
136
137static const char direction_strings[][4] = {
138 "ltr",
139 "rtl",
140 "ttb",
141 "btt"
142};
143
144/**
145 * hb_direction_from_string:
146 * @str: (array length=len) (element-type uint8_t): String to convert
147 * @len: Length of @str, or -1 if it is `NULL`-terminated
148 *
149 * Converts a string to an #hb_direction_t.
150 *
151 * Matching is loose and applies only to the first letter. For
152 * examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.
153 *
154 * Unmatched strings will return #HB_DIRECTION_INVALID.
155 *
156 * Return value: The #hb_direction_t matching @str
157 *
158 * Since: 0.9.2
159 **/
160hb_direction_t
161hb_direction_from_string (const char *str, int len)
162{
163 if (unlikely (!str || !len || !*str))
164 return HB_DIRECTION_INVALID;
165
166 /* Lets match loosely: just match the first letter, such that
167 * all of "ltr", "left-to-right", etc work!
168 */
169 char c = TOLOWER (str[0]);
170 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
171 if (c == direction_strings[i][0])
172 return (hb_direction_t) (HB_DIRECTION_LTR + i);
173
174 return HB_DIRECTION_INVALID;
175}
176
177/**
178 * hb_direction_to_string:
179 * @direction: The #hb_direction_t to convert
180 *
181 * Converts an #hb_direction_t to a string.
182 *
183 * Return value: (transfer none): The string corresponding to @direction
184 *
185 * Since: 0.9.2
186 **/
187const char *
188hb_direction_to_string (hb_direction_t direction)
189{
190 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
191 < ARRAY_LENGTH (direction_strings)))
192 return direction_strings[direction - HB_DIRECTION_LTR];
193
194 return "invalid";
195}
196
197
198/* hb_language_t */
199
200struct hb_language_impl_t {
201 const char s[1];
202};
203
204static const char canon_map[256] = {
205 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
206 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
208 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
209 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
210 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
211 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
212 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
213};
214
215static bool
216lang_equal (hb_language_t v1,
217 const void *v2)
218{
219 const unsigned char *p1 = (const unsigned char *) v1;
220 const unsigned char *p2 = (const unsigned char *) v2;
221
222 while (*p1 && *p1 == canon_map[*p2]) {
223 p1++;
224 p2++;
225 }
226
227 return *p1 == canon_map[*p2];
228}
229
230#if 0
231static unsigned int
232lang_hash (const void *key)
233{
234 const unsigned char *p = key;
235 unsigned int h = 0;
236 while (canon_map[*p])
237 {
238 h = (h << 5) - h + canon_map[*p];
239 p++;
240 }
241
242 return h;
243}
244#endif
245
246
247struct hb_language_item_t {
248
249 struct hb_language_item_t *next;
250 hb_language_t lang;
251
252 bool operator == (const char *s) const
253 { return lang_equal (lang, s); }
254
255 hb_language_item_t & operator = (const char *s)
256 {
257 /* We can't call strdup(), because we allow custom allocators. */
258 size_t len = strlen(s) + 1;
259 lang = (hb_language_t) hb_malloc(len);
260 if (likely (lang))
261 {
262 hb_memcpy((unsigned char *) lang, s, len);
263 for (unsigned char *p = (unsigned char *) lang; *p; p++)
264 *p = canon_map[*p];
265 }
266
267 return *this;
268 }
269
270 void fini () { hb_free ((void *) lang); }
271};
272
273
274/* Thread-safe lockfree language list */
275
276static hb_atomic_ptr_t <hb_language_item_t> langs;
277
278static inline void
279free_langs ()
280{
281retry:
282 hb_language_item_t *first_lang = langs;
283 if (unlikely (!langs.cmpexch (first_lang, nullptr)))
284 goto retry;
285
286 while (first_lang) {
287 hb_language_item_t *next = first_lang->next;
288 first_lang->fini ();
289 hb_free (first_lang);
290 first_lang = next;
291 }
292}
293
294static hb_language_item_t *
295lang_find_or_insert (const char *key)
296{
297retry:
298 hb_language_item_t *first_lang = langs;
299
300 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
301 if (*lang == key)
302 return lang;
303
304 /* Not found; allocate one. */
305 hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));
306 if (unlikely (!lang))
307 return nullptr;
308 lang->next = first_lang;
309 *lang = key;
310 if (unlikely (!lang->lang))
311 {
312 hb_free (lang);
313 return nullptr;
314 }
315
316 if (unlikely (!langs.cmpexch (first_lang, lang)))
317 {
318 lang->fini ();
319 hb_free (lang);
320 goto retry;
321 }
322
323 if (!first_lang)
324 hb_atexit (free_langs); /* First person registers atexit() callback. */
325
326 return lang;
327}
328
329
330/**
331 * hb_language_from_string:
332 * @str: (array length=len) (element-type uint8_t): a string representing
333 * a BCP 47 language tag
334 * @len: length of the @str, or -1 if it is `NULL`-terminated.
335 *
336 * Converts @str representing a BCP 47 language tag to the corresponding
337 * #hb_language_t.
338 *
339 * Return value: (transfer none):
340 * The #hb_language_t corresponding to the BCP 47 language tag.
341 *
342 * Since: 0.9.2
343 **/
344hb_language_t
345hb_language_from_string (const char *str, int len)
346{
347 if (!str || !len || !*str)
348 return HB_LANGUAGE_INVALID;
349
350 hb_language_item_t *item = nullptr;
351 if (len >= 0)
352 {
353 /* NUL-terminate it. */
354 char strbuf[64];
355 len = hb_min (len, (int) sizeof (strbuf) - 1);
356 hb_memcpy (strbuf, str, len);
357 strbuf[len] = '\0';
358 item = lang_find_or_insert (strbuf);
359 }
360 else
361 item = lang_find_or_insert (str);
362
363 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
364}
365
366/**
367 * hb_language_to_string:
368 * @language: The #hb_language_t to convert
369 *
370 * Converts an #hb_language_t to a string.
371 *
372 * Return value: (transfer none):
373 * A `NULL`-terminated string representing the @language. Must not be freed by
374 * the caller.
375 *
376 * Since: 0.9.2
377 **/
378const char *
379hb_language_to_string (hb_language_t language)
380{
381 if (unlikely (!language)) return nullptr;
382
383 return language->s;
384}
385
386/**
387 * hb_language_get_default:
388 *
389 * Fetch the default language from current locale.
390 *
391 * <note>Note that the first time this function is called, it calls
392 * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying
393 * setlocale function is, in many implementations, NOT threadsafe. To avoid
394 * problems, call this function once before multiple threads can call it.
395 * This function is only used from hb_buffer_guess_segment_properties() by
396 * HarfBuzz itself.</note>
397 *
398 * Return value: (transfer none): The default language of the locale as
399 * an #hb_language_t
400 *
401 * Since: 0.9.2
402 **/
403hb_language_t
404hb_language_get_default ()
405{
406 static hb_atomic_ptr_t <hb_language_t> default_language;
407
408 hb_language_t language = default_language;
409 if (unlikely (language == HB_LANGUAGE_INVALID))
410 {
411 language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1);
412 (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
413 }
414
415 return language;
416}
417
418/**
419 * hb_language_matches:
420 * @language: The #hb_language_t to work on
421 * @specific: Another #hb_language_t
422 *
423 * Check whether a second language tag is the same or a more
424 * specific version of the provided language tag. For example,
425 * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR".
426 *
427 * Return value: `true` if languages match, `false` otherwise.
428 *
429 * Since: 5.0.0
430 **/
431hb_bool_t
432hb_language_matches (hb_language_t language,
433 hb_language_t specific)
434{
435 if (language == specific) return true;
436 if (!language || !specific) return false;
437
438 const char *l = language->s;
439 const char *s = specific->s;
440 unsigned ll = strlen (l);
441 unsigned sl = strlen (s);
442
443 if (ll > sl)
444 return false;
445
446 return strncmp (l, s, ll) == 0 &&
447 (s[ll] == '\0' || s[ll] == '-');
448}
449
450
451/* hb_script_t */
452
453/**
454 * hb_script_from_iso15924_tag:
455 * @tag: an #hb_tag_t representing an ISO 15924 tag.
456 *
457 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
458 *
459 * Return value:
460 * An #hb_script_t corresponding to the ISO 15924 tag.
461 *
462 * Since: 0.9.2
463 **/
464hb_script_t
465hb_script_from_iso15924_tag (hb_tag_t tag)
466{
467 if (unlikely (tag == HB_TAG_NONE))
468 return HB_SCRIPT_INVALID;
469
470 /* Be lenient, adjust case (one capital letter followed by three small letters) */
471 tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
472
473 switch (tag) {
474
475 /* These graduated from the 'Q' private-area codes, but
476 * the old code is still aliased by Unicode, and the Qaai
477 * one in use by ICU. */
478 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
479 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
480
481 /* Script variants from https://unicode.org/iso15924/ */
482 case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;
483 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
484 case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;
485 case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;
486 case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;
487 case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;
488 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
489 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
490 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
491 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
492 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
493 }
494
495 /* If it looks right, just use the tag as a script */
496 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
497 return (hb_script_t) tag;
498
499 /* Otherwise, return unknown */
500 return HB_SCRIPT_UNKNOWN;
501}
502
503/**
504 * hb_script_from_string:
505 * @str: (array length=len) (element-type uint8_t): a string representing an
506 * ISO 15924 tag.
507 * @len: length of the @str, or -1 if it is `NULL`-terminated.
508 *
509 * Converts a string @str representing an ISO 15924 script tag to a
510 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
511 * hb_script_from_iso15924_tag().
512 *
513 * Return value:
514 * An #hb_script_t corresponding to the ISO 15924 tag.
515 *
516 * Since: 0.9.2
517 **/
518hb_script_t
519hb_script_from_string (const char *str, int len)
520{
521 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
522}
523
524/**
525 * hb_script_to_iso15924_tag:
526 * @script: an #hb_script_t to convert.
527 *
528 * Converts an #hb_script_t to a corresponding ISO 15924 script tag.
529 *
530 * Return value:
531 * An #hb_tag_t representing an ISO 15924 script tag.
532 *
533 * Since: 0.9.2
534 **/
535hb_tag_t
536hb_script_to_iso15924_tag (hb_script_t script)
537{
538 return (hb_tag_t) script;
539}
540
541/**
542 * hb_script_get_horizontal_direction:
543 * @script: The #hb_script_t to query
544 *
545 * Fetches the #hb_direction_t of a script when it is
546 * set horizontally. All right-to-left scripts will return
547 * #HB_DIRECTION_RTL. All left-to-right scripts will return
548 * #HB_DIRECTION_LTR. Scripts that can be written either
549 * horizontally or vertically will return #HB_DIRECTION_INVALID.
550 * Unknown scripts will return #HB_DIRECTION_LTR.
551 *
552 * Return value: The horizontal #hb_direction_t of @script
553 *
554 * Since: 0.9.2
555 **/
556hb_direction_t
557hb_script_get_horizontal_direction (hb_script_t script)
558{
559 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
560 switch ((hb_tag_t) script)
561 {
562 /* Unicode-1.1 additions */
563 case HB_SCRIPT_ARABIC:
564 case HB_SCRIPT_HEBREW:
565
566 /* Unicode-3.0 additions */
567 case HB_SCRIPT_SYRIAC:
568 case HB_SCRIPT_THAANA:
569
570 /* Unicode-4.0 additions */
571 case HB_SCRIPT_CYPRIOT:
572
573 /* Unicode-4.1 additions */
574 case HB_SCRIPT_KHAROSHTHI:
575
576 /* Unicode-5.0 additions */
577 case HB_SCRIPT_PHOENICIAN:
578 case HB_SCRIPT_NKO:
579
580 /* Unicode-5.1 additions */
581 case HB_SCRIPT_LYDIAN:
582
583 /* Unicode-5.2 additions */
584 case HB_SCRIPT_AVESTAN:
585 case HB_SCRIPT_IMPERIAL_ARAMAIC:
586 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
587 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
588 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
589 case HB_SCRIPT_OLD_TURKIC:
590 case HB_SCRIPT_SAMARITAN:
591
592 /* Unicode-6.0 additions */
593 case HB_SCRIPT_MANDAIC:
594
595 /* Unicode-6.1 additions */
596 case HB_SCRIPT_MEROITIC_CURSIVE:
597 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
598
599 /* Unicode-7.0 additions */
600 case HB_SCRIPT_MANICHAEAN:
601 case HB_SCRIPT_MENDE_KIKAKUI:
602 case HB_SCRIPT_NABATAEAN:
603 case HB_SCRIPT_OLD_NORTH_ARABIAN:
604 case HB_SCRIPT_PALMYRENE:
605 case HB_SCRIPT_PSALTER_PAHLAVI:
606
607 /* Unicode-8.0 additions */
608 case HB_SCRIPT_HATRAN:
609
610 /* Unicode-9.0 additions */
611 case HB_SCRIPT_ADLAM:
612
613 /* Unicode-11.0 additions */
614 case HB_SCRIPT_HANIFI_ROHINGYA:
615 case HB_SCRIPT_OLD_SOGDIAN:
616 case HB_SCRIPT_SOGDIAN:
617
618 /* Unicode-12.0 additions */
619 case HB_SCRIPT_ELYMAIC:
620
621 /* Unicode-13.0 additions */
622 case HB_SCRIPT_CHORASMIAN:
623 case HB_SCRIPT_YEZIDI:
624
625 /* Unicode-14.0 additions */
626 case HB_SCRIPT_OLD_UYGHUR:
627
628 return HB_DIRECTION_RTL;
629
630
631 /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
632 case HB_SCRIPT_OLD_HUNGARIAN:
633 case HB_SCRIPT_OLD_ITALIC:
634 case HB_SCRIPT_RUNIC:
635 case HB_SCRIPT_TIFINAGH:
636
637 return HB_DIRECTION_INVALID;
638 }
639
640 return HB_DIRECTION_LTR;
641}
642
643
644/* hb_version */
645
646
647/**
648 * SECTION:hb-version
649 * @title: hb-version
650 * @short_description: Information about the version of HarfBuzz in use
651 * @include: hb.h
652 *
653 * These functions and macros allow accessing version of the HarfBuzz
654 * library used at compile- as well as run-time, and to direct code
655 * conditionally based on those versions, again, at compile- or run-time.
656 **/
657
658
659/**
660 * hb_version:
661 * @major: (out): Library major version component
662 * @minor: (out): Library minor version component
663 * @micro: (out): Library micro version component
664 *
665 * Returns library version as three integer components.
666 *
667 * Since: 0.9.2
668 **/
669void
670hb_version (unsigned int *major,
671 unsigned int *minor,
672 unsigned int *micro)
673{
674 *major = HB_VERSION_MAJOR;
675 *minor = HB_VERSION_MINOR;
676 *micro = HB_VERSION_MICRO;
677}
678
679/**
680 * hb_version_string:
681 *
682 * Returns library version as a string with three components.
683 *
684 * Return value: Library version string
685 *
686 * Since: 0.9.2
687 **/
688const char *
689hb_version_string ()
690{
691 return HB_VERSION_STRING;
692}
693
694/**
695 * hb_version_atleast:
696 * @major: Library major version component
697 * @minor: Library minor version component
698 * @micro: Library micro version component
699 *
700 * Tests the library version against a minimum value,
701 * as three integer components.
702 *
703 * Return value: `true` if the library is equal to or greater than
704 * the test value, `false` otherwise
705 *
706 * Since: 0.9.30
707 **/
708hb_bool_t
709hb_version_atleast (unsigned int major,
710 unsigned int minor,
711 unsigned int micro)
712{
713 return HB_VERSION_ATLEAST (major, minor, micro);
714}
715
716
717
718/* hb_feature_t and hb_variation_t */
719
720static bool
721parse_space (const char **pp, const char *end)
722{
723 while (*pp < end && ISSPACE (**pp))
724 (*pp)++;
725 return true;
726}
727
728static bool
729parse_char (const char **pp, const char *end, char c)
730{
731 parse_space (pp, end);
732
733 if (*pp == end || **pp != c)
734 return false;
735
736 (*pp)++;
737 return true;
738}
739
740static bool
741parse_uint (const char **pp, const char *end, unsigned int *pv)
742{
743 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
744 * such that -1 turns into "big number"... */
745 int v;
746 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
747
748 *pv = v;
749 return true;
750}
751
752static bool
753parse_uint32 (const char **pp, const char *end, uint32_t *pv)
754{
755 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
756 * such that -1 turns into "big number"... */
757 int v;
758 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
759
760 *pv = v;
761 return true;
762}
763
764static bool
765parse_bool (const char **pp, const char *end, uint32_t *pv)
766{
767 parse_space (pp, end);
768
769 const char *p = *pp;
770 while (*pp < end && ISALPHA(**pp))
771 (*pp)++;
772
773 /* CSS allows on/off as aliases 1/0. */
774 if (*pp - p == 2
775 && TOLOWER (p[0]) == 'o'
776 && TOLOWER (p[1]) == 'n')
777 *pv = 1;
778 else if (*pp - p == 3
779 && TOLOWER (p[0]) == 'o'
780 && TOLOWER (p[1]) == 'f'
781 && TOLOWER (p[2]) == 'f')
782 *pv = 0;
783 else
784 return false;
785
786 return true;
787}
788
789/* hb_feature_t */
790
791static bool
792parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
793{
794 if (parse_char (pp, end, '-'))
795 feature->value = 0;
796 else {
797 parse_char (pp, end, '+');
798 feature->value = 1;
799 }
800
801 return true;
802}
803
804static bool
805parse_tag (const char **pp, const char *end, hb_tag_t *tag)
806{
807 parse_space (pp, end);
808
809 char quote = 0;
810
811 if (*pp < end && (**pp == '\'' || **pp == '"'))
812 {
813 quote = **pp;
814 (*pp)++;
815 }
816
817 const char *p = *pp;
818 while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
819 (*pp)++;
820
821 if (p == *pp || *pp - p > 4)
822 return false;
823
824 *tag = hb_tag_from_string (p, *pp - p);
825
826 if (quote)
827 {
828 /* CSS expects exactly four bytes. And we only allow quotations for
829 * CSS compatibility. So, enforce the length. */
830 if (*pp - p != 4)
831 return false;
832 if (*pp == end || **pp != quote)
833 return false;
834 (*pp)++;
835 }
836
837 return true;
838}
839
840static bool
841parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
842{
843 parse_space (pp, end);
844
845 bool has_start;
846
847 feature->start = HB_FEATURE_GLOBAL_START;
848 feature->end = HB_FEATURE_GLOBAL_END;
849
850 if (!parse_char (pp, end, '['))
851 return true;
852
853 has_start = parse_uint (pp, end, &feature->start);
854
855 if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
856 parse_uint (pp, end, &feature->end);
857 } else {
858 if (has_start)
859 feature->end = feature->start + 1;
860 }
861
862 return parse_char (pp, end, ']');
863}
864
865static bool
866parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
867{
868 bool had_equal = parse_char (pp, end, '=');
869 bool had_value = parse_uint32 (pp, end, &feature->value) ||
870 parse_bool (pp, end, &feature->value);
871 /* CSS doesn't use equal-sign between tag and value.
872 * If there was an equal-sign, then there *must* be a value.
873 * A value without an equal-sign is ok, but not required. */
874 return !had_equal || had_value;
875}
876
877static bool
878parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
879{
880 return parse_feature_value_prefix (pp, end, feature) &&
881 parse_tag (pp, end, &feature->tag) &&
882 parse_feature_indices (pp, end, feature) &&
883 parse_feature_value_postfix (pp, end, feature) &&
884 parse_space (pp, end) &&
885 *pp == end;
886}
887
888/**
889 * hb_feature_from_string:
890 * @str: (array length=len) (element-type uint8_t): a string to parse
891 * @len: length of @str, or -1 if string is `NULL` terminated
892 * @feature: (out): the #hb_feature_t to initialize with the parsed values
893 *
894 * Parses a string into a #hb_feature_t.
895 *
896 * The format for specifying feature strings follows. All valid CSS
897 * font-feature-settings values other than 'normal' and the global values are
898 * also accepted, though not documented below. CSS string escapes are not
899 * supported.
900 *
901 * The range indices refer to the positions between Unicode characters. The
902 * position before the first character is always 0.
903 *
904 * The format is Python-esque. Here is how it all works:
905 *
906 * <informaltable pgwide='1' align='left' frame='none'>
907 * <tgroup cols='5'>
908 * <thead>
909 * <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
910 * </thead>
911 * <tbody>
912 * <row><entry>Setting value:</entry></row>
913 * <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
914 * <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
915 * <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
916 * <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
917 * <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
918 * <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>
919 * <row><entry>Setting index:</entry></row>
920 * <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
921 * <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
922 * <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>
923 * <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>
924 * <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>
925 * <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
926 * <row><entry>Mixing it all:</entry></row>
927 * <row><entry>aalt[3:5]=2</entry> <entry>2</entry> <entry>3</entry> <entry>5</entry> <entry>Turn 2nd alternate on for range</entry></row>
928 * </tbody>
929 * </tgroup>
930 * </informaltable>
931 *
932 * Return value:
933 * `true` if @str is successfully parsed, `false` otherwise
934 *
935 * Since: 0.9.5
936 **/
937hb_bool_t
938hb_feature_from_string (const char *str, int len,
939 hb_feature_t *feature)
940{
941 hb_feature_t feat;
942
943 if (len < 0)
944 len = strlen (str);
945
946 if (likely (parse_one_feature (&str, str + len, &feat)))
947 {
948 if (feature)
949 *feature = feat;
950 return true;
951 }
952
953 if (feature)
954 hb_memset (feature, 0, sizeof (*feature));
955 return false;
956}
957
958/**
959 * hb_feature_to_string:
960 * @feature: an #hb_feature_t to convert
961 * @buf: (array length=size) (out): output string
962 * @size: the allocated size of @buf
963 *
964 * Converts a #hb_feature_t into a `NULL`-terminated string in the format
965 * understood by hb_feature_from_string(). The client in responsible for
966 * allocating big enough size for @buf, 128 bytes is more than enough.
967 *
968 * Since: 0.9.5
969 **/
970void
971hb_feature_to_string (hb_feature_t *feature,
972 char *buf, unsigned int size)
973{
974 if (unlikely (!size)) return;
975
976 char s[128];
977 unsigned int len = 0;
978 if (feature->value == 0)
979 s[len++] = '-';
980 hb_tag_to_string (feature->tag, s + len);
981 len += 4;
982 while (len && s[len - 1] == ' ')
983 len--;
984 if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
985 {
986 s[len++] = '[';
987 if (feature->start)
988 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
989 if (feature->end != feature->start + 1) {
990 s[len++] = ':';
991 if (feature->end != HB_FEATURE_GLOBAL_END)
992 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
993 }
994 s[len++] = ']';
995 }
996 if (feature->value > 1)
997 {
998 s[len++] = '=';
999 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
1000 }
1001 assert (len < ARRAY_LENGTH (s));
1002 len = hb_min (len, size - 1);
1003 hb_memcpy (buf, s, len);
1004 buf[len] = '\0';
1005}
1006
1007/* hb_variation_t */
1008
1009static bool
1010parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
1011{
1012 parse_char (pp, end, '='); /* Optional. */
1013 double v;
1014 if (unlikely (!hb_parse_double (pp, end, &v))) return false;
1015
1016 variation->value = v;
1017 return true;
1018}
1019
1020static bool
1021parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
1022{
1023 return parse_tag (pp, end, &variation->tag) &&
1024 parse_variation_value (pp, end, variation) &&
1025 parse_space (pp, end) &&
1026 *pp == end;
1027}
1028
1029/**
1030 * hb_variation_from_string:
1031 * @str: (array length=len) (element-type uint8_t): a string to parse
1032 * @len: length of @str, or -1 if string is `NULL` terminated
1033 * @variation: (out): the #hb_variation_t to initialize with the parsed values
1034 *
1035 * Parses a string into a #hb_variation_t.
1036 *
1037 * The format for specifying variation settings follows. All valid CSS
1038 * font-variation-settings values other than 'normal' and 'inherited' are also
1039 * accepted, though, not documented below.
1040 *
1041 * The format is a tag, optionally followed by an equals sign, followed by a
1042 * number. For example `wght=500`, or `slnt=-7.5`.
1043 *
1044 * Return value:
1045 * `true` if @str is successfully parsed, `false` otherwise
1046 *
1047 * Since: 1.4.2
1048 */
1049hb_bool_t
1050hb_variation_from_string (const char *str, int len,
1051 hb_variation_t *variation)
1052{
1053 hb_variation_t var;
1054
1055 if (len < 0)
1056 len = strlen (str);
1057
1058 if (likely (parse_one_variation (&str, str + len, &var)))
1059 {
1060 if (variation)
1061 *variation = var;
1062 return true;
1063 }
1064
1065 if (variation)
1066 hb_memset (variation, 0, sizeof (*variation));
1067 return false;
1068}
1069
1070#ifndef HB_NO_SETLOCALE
1071
1072static inline void free_static_C_locale ();
1073
1074static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,
1075 hb_C_locale_lazy_loader_t>
1076{
1077 static hb_locale_t create ()
1078 {
1079 hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
1080 if (!l)
1081 return l;
1082
1083 hb_atexit (free_static_C_locale);
1084
1085 return l;
1086 }
1087 static void destroy (hb_locale_t l)
1088 {
1089 freelocale (l);
1090 }
1091 static hb_locale_t get_null ()
1092 {
1093 return (hb_locale_t) 0;
1094 }
1095} static_C_locale;
1096
1097static inline
1098void free_static_C_locale ()
1099{
1100 static_C_locale.free_instance ();
1101}
1102
1103static hb_locale_t
1104get_C_locale ()
1105{
1106 return static_C_locale.get_unconst ();
1107}
1108
1109#endif
1110
1111/**
1112 * hb_variation_to_string:
1113 * @variation: an #hb_variation_t to convert
1114 * @buf: (array length=size) (out caller-allocates): output string
1115 * @size: the allocated size of @buf
1116 *
1117 * Converts an #hb_variation_t into a `NULL`-terminated string in the format
1118 * understood by hb_variation_from_string(). The client in responsible for
1119 * allocating big enough size for @buf, 128 bytes is more than enough.
1120 *
1121 * Since: 1.4.2
1122 */
1123void
1124hb_variation_to_string (hb_variation_t *variation,
1125 char *buf, unsigned int size)
1126{
1127 if (unlikely (!size)) return;
1128
1129 char s[128];
1130 unsigned int len = 0;
1131 hb_tag_to_string (variation->tag, s + len);
1132 len += 4;
1133 while (len && s[len - 1] == ' ')
1134 len--;
1135 s[len++] = '=';
1136
1137 hb_locale_t oldlocale HB_UNUSED;
1138 oldlocale = hb_uselocale (get_C_locale ());
1139 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
1140 (void) hb_uselocale (oldlocale);
1141
1142 assert (len < ARRAY_LENGTH (s));
1143 len = hb_min (len, size - 1);
1144 hb_memcpy (buf, s, len);
1145 buf[len] = '\0';
1146}
1147
1148/**
1149 * hb_color_get_alpha:
1150 * @color: an #hb_color_t we are interested in its channels.
1151 *
1152 * Fetches the alpha channel of the given @color.
1153 *
1154 * Return value: Alpha channel value
1155 *
1156 * Since: 2.1.0
1157 */
1158uint8_t
1159(hb_color_get_alpha) (hb_color_t color)
1160{
1161 return hb_color_get_alpha (color);
1162}
1163
1164/**
1165 * hb_color_get_red:
1166 * @color: an #hb_color_t we are interested in its channels.
1167 *
1168 * Fetches the red channel of the given @color.
1169 *
1170 * Return value: Red channel value
1171 *
1172 * Since: 2.1.0
1173 */
1174uint8_t
1175(hb_color_get_red) (hb_color_t color)
1176{
1177 return hb_color_get_red (color);
1178}
1179
1180/**
1181 * hb_color_get_green:
1182 * @color: an #hb_color_t we are interested in its channels.
1183 *
1184 * Fetches the green channel of the given @color.
1185 *
1186 * Return value: Green channel value
1187 *
1188 * Since: 2.1.0
1189 */
1190uint8_t
1191(hb_color_get_green) (hb_color_t color)
1192{
1193 return hb_color_get_green (color);
1194}
1195
1196/**
1197 * hb_color_get_blue:
1198 * @color: an #hb_color_t we are interested in its channels.
1199 *
1200 * Fetches the blue channel of the given @color.
1201 *
1202 * Return value: Blue channel value
1203 *
1204 * Since: 2.1.0
1205 */
1206uint8_t
1207(hb_color_get_blue) (hb_color_t color)
1208{
1209 return hb_color_get_blue (color);
1210}
1211
1212
1213/* If there is no visibility control, then hb-static.cc will NOT
1214 * define anything. Instead, we get it to define one set in here
1215 * only, so only libharfbuzz.so defines them, not other libs. */
1216#ifdef HB_NO_VISIBILITY
1217#undef HB_NO_VISIBILITY
1218#include "hb-static.cc"
1219#define HB_NO_VISIBILITY 1
1220#endif
1221