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