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.set_relaxed (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 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 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 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_script_t */
446
447 /**
448 * hb_script_from_iso15924_tag:
449 * @tag: an #hb_tag_t representing an ISO 15924 tag.
450 *
451 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
452 *
453 * Return value:
454 * An #hb_script_t corresponding to the ISO 15924 tag.
455 *
456 * Since: 0.9.2
457 **/
458 hb_script_t
hb_script_from_iso15924_tag(hb_tag_t tag)459 hb_script_from_iso15924_tag (hb_tag_t tag)
460 {
461 if (unlikely (tag == HB_TAG_NONE))
462 return HB_SCRIPT_INVALID;
463
464 /* Be lenient, adjust case (one capital letter followed by three small letters) */
465 tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
466
467 switch (tag) {
468
469 /* These graduated from the 'Q' private-area codes, but
470 * the old code is still aliased by Unicode, and the Qaai
471 * one in use by ICU. */
472 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
473 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
474
475 /* Script variants from https://unicode.org/iso15924/ */
476 case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;
477 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
478 case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;
479 case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;
480 case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;
481 case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;
482 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
483 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
484 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
485 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
486 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
487 }
488
489 /* If it looks right, just use the tag as a script */
490 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
491 return (hb_script_t) tag;
492
493 /* Otherwise, return unknown */
494 return HB_SCRIPT_UNKNOWN;
495 }
496
497 /**
498 * hb_script_from_string:
499 * @str: (array length=len) (element-type uint8_t): a string representing an
500 * ISO 15924 tag.
501 * @len: length of the @str, or -1 if it is %NULL-terminated.
502 *
503 * Converts a string @str representing an ISO 15924 script tag to a
504 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
505 * hb_script_from_iso15924_tag().
506 *
507 * Return value:
508 * An #hb_script_t corresponding to the ISO 15924 tag.
509 *
510 * Since: 0.9.2
511 **/
512 hb_script_t
hb_script_from_string(const char * str,int len)513 hb_script_from_string (const char *str, int len)
514 {
515 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
516 }
517
518 /**
519 * hb_script_to_iso15924_tag:
520 * @script: an #hb_script_t to convert.
521 *
522 * Converts an #hb_script_t to a corresponding ISO 15924 script tag.
523 *
524 * Return value:
525 * An #hb_tag_t representing an ISO 15924 script tag.
526 *
527 * Since: 0.9.2
528 **/
529 hb_tag_t
hb_script_to_iso15924_tag(hb_script_t script)530 hb_script_to_iso15924_tag (hb_script_t script)
531 {
532 return (hb_tag_t) script;
533 }
534
535 /**
536 * hb_script_get_horizontal_direction:
537 * @script: The #hb_script_t to query
538 *
539 * Fetches the #hb_direction_t of a script when it is
540 * set horizontally. All right-to-left scripts will return
541 * #HB_DIRECTION_RTL. All left-to-right scripts will return
542 * #HB_DIRECTION_LTR. Scripts that can be written either
543 * horizontally or vertically will return #HB_DIRECTION_INVALID.
544 * Unknown scripts will return #HB_DIRECTION_LTR.
545 *
546 * Return value: The horizontal #hb_direction_t of @script
547 *
548 * Since: 0.9.2
549 **/
550 hb_direction_t
hb_script_get_horizontal_direction(hb_script_t script)551 hb_script_get_horizontal_direction (hb_script_t script)
552 {
553 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
554 switch ((hb_tag_t) script)
555 {
556 /* Unicode-1.1 additions */
557 case HB_SCRIPT_ARABIC:
558 case HB_SCRIPT_HEBREW:
559
560 /* Unicode-3.0 additions */
561 case HB_SCRIPT_SYRIAC:
562 case HB_SCRIPT_THAANA:
563
564 /* Unicode-4.0 additions */
565 case HB_SCRIPT_CYPRIOT:
566
567 /* Unicode-4.1 additions */
568 case HB_SCRIPT_KHAROSHTHI:
569
570 /* Unicode-5.0 additions */
571 case HB_SCRIPT_PHOENICIAN:
572 case HB_SCRIPT_NKO:
573
574 /* Unicode-5.1 additions */
575 case HB_SCRIPT_LYDIAN:
576
577 /* Unicode-5.2 additions */
578 case HB_SCRIPT_AVESTAN:
579 case HB_SCRIPT_IMPERIAL_ARAMAIC:
580 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
581 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
582 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
583 case HB_SCRIPT_OLD_TURKIC:
584 case HB_SCRIPT_SAMARITAN:
585
586 /* Unicode-6.0 additions */
587 case HB_SCRIPT_MANDAIC:
588
589 /* Unicode-6.1 additions */
590 case HB_SCRIPT_MEROITIC_CURSIVE:
591 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
592
593 /* Unicode-7.0 additions */
594 case HB_SCRIPT_MANICHAEAN:
595 case HB_SCRIPT_MENDE_KIKAKUI:
596 case HB_SCRIPT_NABATAEAN:
597 case HB_SCRIPT_OLD_NORTH_ARABIAN:
598 case HB_SCRIPT_PALMYRENE:
599 case HB_SCRIPT_PSALTER_PAHLAVI:
600
601 /* Unicode-8.0 additions */
602 case HB_SCRIPT_HATRAN:
603
604 /* Unicode-9.0 additions */
605 case HB_SCRIPT_ADLAM:
606
607 /* Unicode-11.0 additions */
608 case HB_SCRIPT_HANIFI_ROHINGYA:
609 case HB_SCRIPT_OLD_SOGDIAN:
610 case HB_SCRIPT_SOGDIAN:
611
612 /* Unicode-12.0 additions */
613 case HB_SCRIPT_ELYMAIC:
614
615 /* Unicode-13.0 additions */
616 case HB_SCRIPT_CHORASMIAN:
617 case HB_SCRIPT_YEZIDI:
618
619 /* Unicode-14.0 additions */
620 case HB_SCRIPT_OLD_UYGHUR:
621
622 return HB_DIRECTION_RTL;
623
624
625 /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
626 case HB_SCRIPT_OLD_HUNGARIAN:
627 case HB_SCRIPT_OLD_ITALIC:
628 case HB_SCRIPT_RUNIC:
629
630 return HB_DIRECTION_INVALID;
631 }
632
633 return HB_DIRECTION_LTR;
634 }
635
636
637 /* hb_version */
638
639
640 /**
641 * SECTION:hb-version
642 * @title: hb-version
643 * @short_description: Information about the version of HarfBuzz in use
644 * @include: hb.h
645 *
646 * These functions and macros allow accessing version of the HarfBuzz
647 * library used at compile- as well as run-time, and to direct code
648 * conditionally based on those versions, again, at compile- or run-time.
649 **/
650
651
652 /**
653 * hb_version:
654 * @major: (out): Library major version component
655 * @minor: (out): Library minor version component
656 * @micro: (out): Library micro version component
657 *
658 * Returns library version as three integer components.
659 *
660 * Since: 0.9.2
661 **/
662 void
hb_version(unsigned int * major,unsigned int * minor,unsigned int * micro)663 hb_version (unsigned int *major,
664 unsigned int *minor,
665 unsigned int *micro)
666 {
667 *major = HB_VERSION_MAJOR;
668 *minor = HB_VERSION_MINOR;
669 *micro = HB_VERSION_MICRO;
670 }
671
672 /**
673 * hb_version_string:
674 *
675 * Returns library version as a string with three components.
676 *
677 * Return value: Library version string
678 *
679 * Since: 0.9.2
680 **/
681 const char *
hb_version_string()682 hb_version_string ()
683 {
684 return HB_VERSION_STRING;
685 }
686
687 /**
688 * hb_version_atleast:
689 * @major: Library major version component
690 * @minor: Library minor version component
691 * @micro: Library micro version component
692 *
693 * Tests the library version against a minimum value,
694 * as three integer components.
695 *
696 * Return value: %true if the library is equal to or greater than
697 * the test value, %false otherwise
698 *
699 * Since: 0.9.30
700 **/
701 hb_bool_t
hb_version_atleast(unsigned int major,unsigned int minor,unsigned int micro)702 hb_version_atleast (unsigned int major,
703 unsigned int minor,
704 unsigned int micro)
705 {
706 return HB_VERSION_ATLEAST (major, minor, micro);
707 }
708
709
710
711 /* hb_feature_t and hb_variation_t */
712
713 static bool
parse_space(const char ** pp,const char * end)714 parse_space (const char **pp, const char *end)
715 {
716 while (*pp < end && ISSPACE (**pp))
717 (*pp)++;
718 return true;
719 }
720
721 static bool
parse_char(const char ** pp,const char * end,char c)722 parse_char (const char **pp, const char *end, char c)
723 {
724 parse_space (pp, end);
725
726 if (*pp == end || **pp != c)
727 return false;
728
729 (*pp)++;
730 return true;
731 }
732
733 static bool
parse_uint(const char ** pp,const char * end,unsigned int * pv)734 parse_uint (const char **pp, const char *end, unsigned int *pv)
735 {
736 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
737 * such that -1 turns into "big number"... */
738 int v;
739 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
740
741 *pv = v;
742 return true;
743 }
744
745 static bool
parse_uint32(const char ** pp,const char * end,uint32_t * pv)746 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
747 {
748 /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
749 * such that -1 turns into "big number"... */
750 int v;
751 if (unlikely (!hb_parse_int (pp, end, &v))) return false;
752
753 *pv = v;
754 return true;
755 }
756
757 static bool
parse_bool(const char ** pp,const char * end,uint32_t * pv)758 parse_bool (const char **pp, const char *end, uint32_t *pv)
759 {
760 parse_space (pp, end);
761
762 const char *p = *pp;
763 while (*pp < end && ISALPHA(**pp))
764 (*pp)++;
765
766 /* CSS allows on/off as aliases 1/0. */
767 if (*pp - p == 2
768 && TOLOWER (p[0]) == 'o'
769 && TOLOWER (p[1]) == 'n')
770 *pv = 1;
771 else if (*pp - p == 3
772 && TOLOWER (p[0]) == 'o'
773 && TOLOWER (p[1]) == 'f'
774 && TOLOWER (p[2]) == 'f')
775 *pv = 0;
776 else
777 return false;
778
779 return true;
780 }
781
782 /* hb_feature_t */
783
784 static bool
parse_feature_value_prefix(const char ** pp,const char * end,hb_feature_t * feature)785 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
786 {
787 if (parse_char (pp, end, '-'))
788 feature->value = 0;
789 else {
790 parse_char (pp, end, '+');
791 feature->value = 1;
792 }
793
794 return true;
795 }
796
797 static bool
parse_tag(const char ** pp,const char * end,hb_tag_t * tag)798 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
799 {
800 parse_space (pp, end);
801
802 char quote = 0;
803
804 if (*pp < end && (**pp == '\'' || **pp == '"'))
805 {
806 quote = **pp;
807 (*pp)++;
808 }
809
810 const char *p = *pp;
811 while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
812 (*pp)++;
813
814 if (p == *pp || *pp - p > 4)
815 return false;
816
817 *tag = hb_tag_from_string (p, *pp - p);
818
819 if (quote)
820 {
821 /* CSS expects exactly four bytes. And we only allow quotations for
822 * CSS compatibility. So, enforce the length. */
823 if (*pp - p != 4)
824 return false;
825 if (*pp == end || **pp != quote)
826 return false;
827 (*pp)++;
828 }
829
830 return true;
831 }
832
833 static bool
parse_feature_indices(const char ** pp,const char * end,hb_feature_t * feature)834 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
835 {
836 parse_space (pp, end);
837
838 bool has_start;
839
840 feature->start = HB_FEATURE_GLOBAL_START;
841 feature->end = HB_FEATURE_GLOBAL_END;
842
843 if (!parse_char (pp, end, '['))
844 return true;
845
846 has_start = parse_uint (pp, end, &feature->start);
847
848 if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
849 parse_uint (pp, end, &feature->end);
850 } else {
851 if (has_start)
852 feature->end = feature->start + 1;
853 }
854
855 return parse_char (pp, end, ']');
856 }
857
858 static bool
parse_feature_value_postfix(const char ** pp,const char * end,hb_feature_t * feature)859 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
860 {
861 bool had_equal = parse_char (pp, end, '=');
862 bool had_value = parse_uint32 (pp, end, &feature->value) ||
863 parse_bool (pp, end, &feature->value);
864 /* CSS doesn't use equal-sign between tag and value.
865 * If there was an equal-sign, then there *must* be a value.
866 * A value without an equal-sign is ok, but not required. */
867 return !had_equal || had_value;
868 }
869
870 static bool
parse_one_feature(const char ** pp,const char * end,hb_feature_t * feature)871 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
872 {
873 return parse_feature_value_prefix (pp, end, feature) &&
874 parse_tag (pp, end, &feature->tag) &&
875 parse_feature_indices (pp, end, feature) &&
876 parse_feature_value_postfix (pp, end, feature) &&
877 parse_space (pp, end) &&
878 *pp == end;
879 }
880
881 /**
882 * hb_feature_from_string:
883 * @str: (array length=len) (element-type uint8_t): a string to parse
884 * @len: length of @str, or -1 if string is %NULL terminated
885 * @feature: (out): the #hb_feature_t to initialize with the parsed values
886 *
887 * Parses a string into a #hb_feature_t.
888 *
889 * The format for specifying feature strings follows. All valid CSS
890 * font-feature-settings values other than 'normal' and the global values are
891 * also accepted, though not documented below. CSS string escapes are not
892 * supported.
893 *
894 * The range indices refer to the positions between Unicode characters. The
895 * position before the first character is always 0.
896 *
897 * The format is Python-esque. Here is how it all works:
898 *
899 * <informaltable pgwide='1' align='left' frame='none'>
900 * <tgroup cols='5'>
901 * <thead>
902 * <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
903 * </thead>
904 * <tbody>
905 * <row><entry>Setting value:</entry></row>
906 * <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
907 * <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
908 * <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
909 * <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row>
910 * <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
911 * <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row>
912 * <row><entry>Setting index:</entry></row>
913 * <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
914 * <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row>
915 * <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row>
916 * <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row>
917 * <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row>
918 * <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
919 * <row><entry>Mixing it all:</entry></row>
920 * <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>
921 * </tbody>
922 * </tgroup>
923 * </informaltable>
924 *
925 * Return value:
926 * %true if @str is successfully parsed, %false otherwise
927 *
928 * Since: 0.9.5
929 **/
930 hb_bool_t
hb_feature_from_string(const char * str,int len,hb_feature_t * feature)931 hb_feature_from_string (const char *str, int len,
932 hb_feature_t *feature)
933 {
934 hb_feature_t feat;
935
936 if (len < 0)
937 len = strlen (str);
938
939 if (likely (parse_one_feature (&str, str + len, &feat)))
940 {
941 if (feature)
942 *feature = feat;
943 return true;
944 }
945
946 if (feature)
947 memset (feature, 0, sizeof (*feature));
948 return false;
949 }
950
951 /**
952 * hb_feature_to_string:
953 * @feature: an #hb_feature_t to convert
954 * @buf: (array length=size) (out): output string
955 * @size: the allocated size of @buf
956 *
957 * Converts a #hb_feature_t into a %NULL-terminated string in the format
958 * understood by hb_feature_from_string(). The client in responsible for
959 * allocating big enough size for @buf, 128 bytes is more than enough.
960 *
961 * Since: 0.9.5
962 **/
963 void
hb_feature_to_string(hb_feature_t * feature,char * buf,unsigned int size)964 hb_feature_to_string (hb_feature_t *feature,
965 char *buf, unsigned int size)
966 {
967 if (unlikely (!size)) return;
968
969 char s[128];
970 unsigned int len = 0;
971 if (feature->value == 0)
972 s[len++] = '-';
973 hb_tag_to_string (feature->tag, s + len);
974 len += 4;
975 while (len && s[len - 1] == ' ')
976 len--;
977 if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
978 {
979 s[len++] = '[';
980 if (feature->start)
981 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
982 if (feature->end != feature->start + 1) {
983 s[len++] = ':';
984 if (feature->end != HB_FEATURE_GLOBAL_END)
985 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
986 }
987 s[len++] = ']';
988 }
989 if (feature->value > 1)
990 {
991 s[len++] = '=';
992 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
993 }
994 assert (len < ARRAY_LENGTH (s));
995 len = hb_min (len, size - 1);
996 memcpy (buf, s, len);
997 buf[len] = '\0';
998 }
999
1000 /* hb_variation_t */
1001
1002 static bool
parse_variation_value(const char ** pp,const char * end,hb_variation_t * variation)1003 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
1004 {
1005 parse_char (pp, end, '='); /* Optional. */
1006 double v;
1007 if (unlikely (!hb_parse_double (pp, end, &v))) return false;
1008
1009 variation->value = v;
1010 return true;
1011 }
1012
1013 static bool
parse_one_variation(const char ** pp,const char * end,hb_variation_t * variation)1014 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
1015 {
1016 return parse_tag (pp, end, &variation->tag) &&
1017 parse_variation_value (pp, end, variation) &&
1018 parse_space (pp, end) &&
1019 *pp == end;
1020 }
1021
1022 /**
1023 * hb_variation_from_string:
1024 * @str: (array length=len) (element-type uint8_t): a string to parse
1025 * @len: length of @str, or -1 if string is %NULL terminated
1026 * @variation: (out): the #hb_variation_t to initialize with the parsed values
1027 *
1028 * Parses a string into a #hb_variation_t.
1029 *
1030 * The format for specifying variation settings follows. All valid CSS
1031 * font-variation-settings values other than 'normal' and 'inherited' are also
1032 * accepted, though, not documented below.
1033 *
1034 * The format is a tag, optionally followed by an equals sign, followed by a
1035 * number. For example `wght=500`, or `slnt=-7.5`.
1036 *
1037 * Return value:
1038 * %true if @str is successfully parsed, %false otherwise
1039 *
1040 * Since: 1.4.2
1041 */
1042 hb_bool_t
hb_variation_from_string(const char * str,int len,hb_variation_t * variation)1043 hb_variation_from_string (const char *str, int len,
1044 hb_variation_t *variation)
1045 {
1046 hb_variation_t var;
1047
1048 if (len < 0)
1049 len = strlen (str);
1050
1051 if (likely (parse_one_variation (&str, str + len, &var)))
1052 {
1053 if (variation)
1054 *variation = var;
1055 return true;
1056 }
1057
1058 if (variation)
1059 memset (variation, 0, sizeof (*variation));
1060 return false;
1061 }
1062
1063 #ifndef HB_NO_SETLOCALE
1064
1065 static inline void free_static_C_locale ();
1066
1067 static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,
1068 hb_C_locale_lazy_loader_t>
1069 {
createhb_C_locale_lazy_loader_t1070 static hb_locale_t create ()
1071 {
1072 hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
1073 if (!l)
1074 return l;
1075
1076 hb_atexit (free_static_C_locale);
1077
1078 return l;
1079 }
destroyhb_C_locale_lazy_loader_t1080 static void destroy (hb_locale_t l)
1081 {
1082 freelocale (l);
1083 }
get_nullhb_C_locale_lazy_loader_t1084 static hb_locale_t get_null ()
1085 {
1086 return (hb_locale_t) 0;
1087 }
1088 } static_C_locale;
1089
1090 static inline
free_static_C_locale()1091 void free_static_C_locale ()
1092 {
1093 static_C_locale.free_instance ();
1094 }
1095
1096 static hb_locale_t
get_C_locale()1097 get_C_locale ()
1098 {
1099 return static_C_locale.get_unconst ();
1100 }
1101
1102 #endif
1103
1104 /**
1105 * hb_variation_to_string:
1106 * @variation: an #hb_variation_t to convert
1107 * @buf: (array length=size) (out): output string
1108 * @size: the allocated size of @buf
1109 *
1110 * Converts an #hb_variation_t into a %NULL-terminated string in the format
1111 * understood by hb_variation_from_string(). The client in responsible for
1112 * allocating big enough size for @buf, 128 bytes is more than enough.
1113 *
1114 * Since: 1.4.2
1115 */
1116 void
hb_variation_to_string(hb_variation_t * variation,char * buf,unsigned int size)1117 hb_variation_to_string (hb_variation_t *variation,
1118 char *buf, unsigned int size)
1119 {
1120 if (unlikely (!size)) return;
1121
1122 char s[128];
1123 unsigned int len = 0;
1124 hb_tag_to_string (variation->tag, s + len);
1125 len += 4;
1126 while (len && s[len - 1] == ' ')
1127 len--;
1128 s[len++] = '=';
1129
1130 hb_locale_t oldlocale HB_UNUSED;
1131 oldlocale = hb_uselocale (get_C_locale ());
1132 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
1133 (void) hb_uselocale (oldlocale);
1134
1135 assert (len < ARRAY_LENGTH (s));
1136 len = hb_min (len, size - 1);
1137 memcpy (buf, s, len);
1138 buf[len] = '\0';
1139 }
1140
1141 /**
1142 * hb_color_get_alpha:
1143 * @color: an #hb_color_t we are interested in its channels.
1144 *
1145 * Fetches the alpha channel of the given @color.
1146 *
1147 * Return value: Alpha channel value
1148 *
1149 * Since: 2.1.0
1150 */
uint8_t(hb_color_get_alpha)1151 uint8_t
1152 (hb_color_get_alpha) (hb_color_t color)
1153 {
1154 return hb_color_get_alpha (color);
1155 }
1156
1157 /**
1158 * hb_color_get_red:
1159 * @color: an #hb_color_t we are interested in its channels.
1160 *
1161 * Fetches the red channel of the given @color.
1162 *
1163 * Return value: Red channel value
1164 *
1165 * Since: 2.1.0
1166 */
uint8_t(hb_color_get_red)1167 uint8_t
1168 (hb_color_get_red) (hb_color_t color)
1169 {
1170 return hb_color_get_red (color);
1171 }
1172
1173 /**
1174 * hb_color_get_green:
1175 * @color: an #hb_color_t we are interested in its channels.
1176 *
1177 * Fetches the green channel of the given @color.
1178 *
1179 * Return value: Green channel value
1180 *
1181 * Since: 2.1.0
1182 */
uint8_t(hb_color_get_green)1183 uint8_t
1184 (hb_color_get_green) (hb_color_t color)
1185 {
1186 return hb_color_get_green (color);
1187 }
1188
1189 /**
1190 * hb_color_get_blue:
1191 * @color: an #hb_color_t we are interested in its channels.
1192 *
1193 * Fetches the blue channel of the given @color.
1194 *
1195 * Return value: Blue channel value
1196 *
1197 * Since: 2.1.0
1198 */
uint8_t(hb_color_get_blue)1199 uint8_t
1200 (hb_color_get_blue) (hb_color_t color)
1201 {
1202 return hb_color_get_blue (color);
1203 }
1204
1205
1206 /* If there is no visibility control, then hb-static.cc will NOT
1207 * define anything. Instead, we get it to define one set in here
1208 * only, so only libharfbuzz.so defines them, not other libs. */
1209 #ifdef HB_NO_VISIBILITY
1210 #undef HB_NO_VISIBILITY
1211 #include "hb-static.cc"
1212 #define HB_NO_VISIBILITY 1
1213 #endif
1214