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