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