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