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