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