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
50 hb_atomic_int_t _hb_options;
51
52 void
_hb_options_init()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 **/
99 hb_tag_t
hb_tag_from_string(const char * str,int len)100 hb_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 **/
127 void
hb_tag_to_string(hb_tag_t tag,char * buf)128 hb_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
139 const 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 **/
157 hb_direction_t
hb_direction_from_string(const char * str,int len)158 hb_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 **/
184 const char *
hb_direction_to_string(hb_direction_t direction)185 hb_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
197 struct hb_language_impl_t {
198 const char s[1];
199 };
200
201 static 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
212 static bool
lang_equal(hb_language_t v1,const void * v2)213 lang_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
228 static unsigned int
229 lang_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
244 struct hb_language_item_t {
245
246 struct hb_language_item_t *next;
247 hb_language_t lang;
248
operator ==hb_language_item_t249 bool operator == (const char *s) const
250 { return lang_equal (lang, s); }
251
operator =hb_language_item_t252 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
finihb_language_item_t269 void fini () { free ((void *) lang); }
270 };
271
272
273 /* Thread-safe lock-free language list */
274
275 static hb_atomic_ptr_t <hb_language_item_t> langs;
276
277 #if HB_USE_ATEXIT
278 static void
free_langs()279 free_langs ()
280 {
281 retry:
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
295 static hb_language_item_t *
lang_find_or_insert(const char * key)296 lang_find_or_insert (const char *key)
297 {
298 retry:
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 **/
347 hb_language_t
hb_language_from_string(const char * str,int len)348 hb_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 **/
381 const char *
hb_language_to_string(hb_language_t language)382 hb_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 **/
405 hb_language_t
hb_language_get_default()406 hb_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 **/
434 hb_script_t
hb_script_from_iso15924_tag(hb_tag_t tag)435 hb_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 **/
483 hb_script_t
hb_script_from_string(const char * str,int len)484 hb_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 **/
500 hb_tag_t
hb_script_to_iso15924_tag(hb_script_t script)501 hb_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 **/
516 hb_direction_t
hb_script_get_horizontal_direction(hb_script_t script)517 hb_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
595 bool
set(hb_user_data_key_t * key,void * data,hb_destroy_func_t destroy,hb_bool_t replace)596 hb_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
616 void *
get(hb_user_data_key_t * key)617 hb_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 **/
650 void
hb_version(unsigned int * major,unsigned int * minor,unsigned int * micro)651 hb_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 **/
669 const char *
hb_version_string()670 hb_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 **/
687 hb_bool_t
hb_version_atleast(unsigned int major,unsigned int minor,unsigned int micro)688 hb_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
699 static bool
parse_space(const char ** pp,const char * end)700 parse_space (const char **pp, const char *end)
701 {
702 while (*pp < end && ISSPACE (**pp))
703 (*pp)++;
704 return true;
705 }
706
707 static bool
parse_char(const char ** pp,const char * end,char c)708 parse_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
719 static bool
parse_uint(const char ** pp,const char * end,unsigned int * pv)720 parse_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
731 static bool
parse_uint32(const char ** pp,const char * end,uint32_t * pv)732 parse_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
743 static bool
parse_bool(const char ** pp,const char * end,uint32_t * pv)744 parse_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
770 static bool
parse_feature_value_prefix(const char ** pp,const char * end,hb_feature_t * feature)771 parse_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
783 static bool
parse_tag(const char ** pp,const char * end,hb_tag_t * tag)784 parse_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
819 static bool
parse_feature_indices(const char ** pp,const char * end,hb_feature_t * feature)820 parse_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
844 static bool
parse_feature_value_postfix(const char ** pp,const char * end,hb_feature_t * feature)845 parse_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
856 static bool
parse_one_feature(const char ** pp,const char * end,hb_feature_t * feature)857 parse_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 **/
916 hb_bool_t
hb_feature_from_string(const char * str,int len,hb_feature_t * feature)917 hb_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 **/
949 void
hb_feature_to_string(hb_feature_t * feature,char * buf,unsigned int size)950 hb_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
988 static bool
parse_variation_value(const char ** pp,const char * end,hb_variation_t * variation)989 parse_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
999 static bool
parse_one_variation(const char ** pp,const char * end,hb_variation_t * variation)1000 parse_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 */
1013 hb_bool_t
hb_variation_from_string(const char * str,int len,hb_variation_t * variation)1014 hb_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 */
1039 void
hb_variation_to_string(hb_variation_t * variation,char * buf,unsigned int size)1040 hb_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 */
uint8_t(hb_color_get_alpha)1068 uint8_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 */
uint8_t(hb_color_get_red)1082 uint8_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 */
uint8_t(hb_color_get_green)1096 uint8_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 */
uint8_t(hb_color_get_blue)1110 uint8_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