Coverage Report

Created: 2024-08-17 06:41

/src/harfbuzz/src/hb-common.cc
Line
Count
Source (jump to first uncovered line)
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
0
#define hb_locale_t locale_t
47
#endif
48
6
#define hb_setlocale setlocale
49
0
#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
74
_hb_options_init ()
75
6
{
76
6
  hb_options_union_t u;
77
6
  u.i = 0;
78
6
  u.opts.initialized = true;
79
80
6
  const char *c = getenv ("HB_OPTIONS");
81
6
  if (c)
82
0
  {
83
0
    while (*c)
84
0
    {
85
0
      const char *p = strchr (c, ':');
86
0
      if (!p)
87
0
  p = c + strlen (c);
88
89
0
#define OPTION(name, symbol) \
90
0
  if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0)
91
92
0
      OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
93
94
0
#undef OPTION
95
96
0
      c = *p ? p + 1 : p;
97
0
    }
98
99
0
  }
100
101
  /* This is idempotent and threadsafe. */
102
6
  _hb_options = u.i;
103
6
}
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
123
hb_tag_from_string (const char *str, int len)
124
23.7k
{
125
23.7k
  char tag[4];
126
23.7k
  unsigned int i;
127
128
23.7k
  if (!str || !len || !*str)
129
0
    return HB_TAG_NONE;
130
131
23.7k
  if (len < 0 || len > 4)
132
0
    len = 4;
133
47.4k
  for (i = 0; i < (unsigned) len && str[i]; i++)
134
23.7k
    tag[i] = str[i];
135
94.8k
  for (; i < 4; i++)
136
71.1k
    tag[i] = ' ';
137
138
23.7k
  return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
139
23.7k
}
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
152
hb_tag_to_string (hb_tag_t tag, char *buf)
153
0
{
154
0
  buf[0] = (char) (uint8_t) (tag >> 24);
155
0
  buf[1] = (char) (uint8_t) (tag >> 16);
156
0
  buf[2] = (char) (uint8_t) (tag >>  8);
157
0
  buf[3] = (char) (uint8_t) (tag >>  0);
158
0
}
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
187
hb_direction_from_string (const char *str, int len)
188
0
{
189
0
  if (unlikely (!str || !len || !*str))
190
0
    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
0
  char c = TOLOWER (str[0]);
196
0
  for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
197
0
    if (c == direction_strings[i][0])
198
0
      return (hb_direction_t) (HB_DIRECTION_LTR + i);
199
200
0
  return HB_DIRECTION_INVALID;
201
0
}
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 *
214
hb_direction_to_string (hb_direction_t direction)
215
0
{
216
0
  if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
217
0
        < ARRAY_LENGTH (direction_strings)))
218
0
    return direction_strings[direction - HB_DIRECTION_LTR];
219
220
0
  return "invalid";
221
0
}
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
242
lang_equal (hb_language_t  v1,
243
      const void    *v2)
244
1.90M
{
245
1.90M
  const unsigned char *p1 = (const unsigned char *) v1;
246
1.90M
  const unsigned char *p2 = (const unsigned char *) v2;
247
248
2.38M
  while (*p1 && *p1 == canon_map[*p2]) {
249
479k
    p1++;
250
479k
    p2++;
251
479k
  }
252
253
1.90M
  return *p1 == canon_map[*p2];
254
1.90M
}
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
278
  bool operator == (const char *s) const
279
1.90M
  { return lang_equal (lang, s); }
280
281
  hb_language_item_t & operator = (const char *s)
282
120
  {
283
    /* We can't call strdup(), because we allow custom allocators. */
284
120
    size_t len = strlen(s) + 1;
285
120
    lang = (hb_language_t) hb_malloc(len);
286
120
    if (likely (lang))
287
118
    {
288
118
      hb_memcpy((unsigned char *) lang, s, len);
289
362
      for (unsigned char *p = (unsigned char *) lang; *p; p++)
290
244
  *p = canon_map[*p];
291
118
    }
292
293
120
    return *this;
294
120
  }
295
296
0
  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
305
free_langs ()
306
0
{
307
0
retry:
308
0
  hb_language_item_t *first_lang = langs;
309
0
  if (unlikely (!langs.cmpexch (first_lang, nullptr)))
310
0
    goto retry;
311
312
0
  while (first_lang) {
313
0
    hb_language_item_t *next = first_lang->next;
314
0
    first_lang->fini ();
315
0
    hb_free (first_lang);
316
0
    first_lang = next;
317
0
  }
318
0
}
319
320
static hb_language_item_t *
321
lang_find_or_insert (const char *key)
322
130k
{
323
130k
retry:
324
130k
  hb_language_item_t *first_lang = langs;
325
326
1.90M
  for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
327
1.90M
    if (*lang == key)
328
130k
      return lang;
329
330
  /* Not found; allocate one. */
331
122
  hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));
332
122
  if (unlikely (!lang))
333
2
    return nullptr;
334
120
  lang->next = first_lang;
335
120
  *lang = key;
336
120
  if (unlikely (!lang->lang))
337
2
  {
338
2
    hb_free (lang);
339
2
    return nullptr;
340
2
  }
341
342
118
  if (unlikely (!langs.cmpexch (first_lang, lang)))
343
0
  {
344
0
    lang->fini ();
345
0
    hb_free (lang);
346
0
    goto retry;
347
0
  }
348
349
118
  if (!first_lang)
350
6
    hb_atexit (free_langs); /* First person registers atexit() callback. */
351
352
118
  return lang;
353
118
}
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
371
hb_language_from_string (const char *str, int len)
372
186k
{
373
186k
  if (!str || !len || !*str)
374
56.2k
    return HB_LANGUAGE_INVALID;
375
376
130k
  hb_language_item_t *item = nullptr;
377
130k
  if (len >= 0)
378
50.8k
  {
379
    /* NUL-terminate it. */
380
50.8k
    char strbuf[64];
381
50.8k
    len = hb_min (len, (int) sizeof (strbuf) - 1);
382
50.8k
    hb_memcpy (strbuf, str, len);
383
50.8k
    strbuf[len] = '\0';
384
50.8k
    item = lang_find_or_insert (strbuf);
385
50.8k
  }
386
79.4k
  else
387
79.4k
    item = lang_find_or_insert (str);
388
389
130k
  return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
390
186k
}
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 *
405
hb_language_to_string (hb_language_t language)
406
28.3k
{
407
28.3k
  if (unlikely (!language)) return nullptr;
408
409
28.3k
  return language->s;
410
28.3k
}
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
430
hb_language_get_default ()
431
33.6k
{
432
33.6k
  static hb_atomic_ptr_t <hb_language_t> default_language;
433
434
33.6k
  hb_language_t language = default_language;
435
33.6k
  if (unlikely (language == HB_LANGUAGE_INVALID))
436
6
  {
437
6
    language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1);
438
6
    (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
439
6
  }
440
441
33.6k
  return language;
442
33.6k
}
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
458
hb_language_matches (hb_language_t language,
459
         hb_language_t specific)
460
15
{
461
15
  if (language == specific) return true;
462
15
  if (!language || !specific) return false;
463
464
15
  const char *l = language->s;
465
15
  const char *s = specific->s;
466
15
  unsigned ll = strlen (l);
467
15
  unsigned sl = strlen (s);
468
469
15
  if (ll > sl)
470
0
    return false;
471
472
15
  return strncmp (l, s, ll) == 0 &&
473
15
   (s[ll] == '\0' || s[ll] == '-');
474
15
}
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
491
hb_script_from_iso15924_tag (hb_tag_t tag)
492
0
{
493
0
  if (unlikely (tag == HB_TAG_NONE))
494
0
    return HB_SCRIPT_INVALID;
495
496
  /* Be lenient, adjust case (one capital letter followed by three small letters) */
497
0
  tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
498
499
0
  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
0
    case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
505
0
    case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
506
507
    /* Script variants from https://unicode.org/iso15924/ */
508
0
    case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;
509
0
    case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
510
0
    case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;
511
0
    case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;
512
0
    case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;
513
0
    case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;
514
0
    case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
515
0
    case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
516
0
    case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
517
0
    case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
518
0
    case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
519
0
  }
520
521
  /* If it looks right, just use the tag as a script */
522
0
  if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
523
0
    return (hb_script_t) tag;
524
525
  /* Otherwise, return unknown */
526
0
  return HB_SCRIPT_UNKNOWN;
527
0
}
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
545
hb_script_from_string (const char *str, int len)
546
0
{
547
0
  return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
548
0
}
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
562
hb_script_to_iso15924_tag (hb_script_t script)
563
0
{
564
0
  return (hb_tag_t) script;
565
0
}
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
583
hb_script_get_horizontal_direction (hb_script_t script)
584
340k
{
585
  /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
586
340k
  switch ((hb_tag_t) script)
587
340k
  {
588
    /* Unicode-1.1 additions */
589
1.33k
    case HB_SCRIPT_ARABIC:
590
1.62k
    case HB_SCRIPT_HEBREW:
591
592
    /* Unicode-3.0 additions */
593
1.82k
    case HB_SCRIPT_SYRIAC:
594
1.82k
    case HB_SCRIPT_THAANA:
595
596
    /* Unicode-4.0 additions */
597
1.84k
    case HB_SCRIPT_CYPRIOT:
598
599
    /* Unicode-4.1 additions */
600
1.85k
    case HB_SCRIPT_KHAROSHTHI:
601
602
    /* Unicode-5.0 additions */
603
1.86k
    case HB_SCRIPT_PHOENICIAN:
604
1.87k
    case HB_SCRIPT_NKO:
605
606
    /* Unicode-5.1 additions */
607
1.87k
    case HB_SCRIPT_LYDIAN:
608
609
    /* Unicode-5.2 additions */
610
1.88k
    case HB_SCRIPT_AVESTAN:
611
1.88k
    case HB_SCRIPT_IMPERIAL_ARAMAIC:
612
1.89k
    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
613
1.89k
    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
614
1.90k
    case HB_SCRIPT_OLD_SOUTH_ARABIAN:
615
1.91k
    case HB_SCRIPT_OLD_TURKIC:
616
2.06k
    case HB_SCRIPT_SAMARITAN:
617
618
    /* Unicode-6.0 additions */
619
2.07k
    case HB_SCRIPT_MANDAIC:
620
621
    /* Unicode-6.1 additions */
622
2.08k
    case HB_SCRIPT_MEROITIC_CURSIVE:
623
2.09k
    case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
624
625
    /* Unicode-7.0 additions */
626
2.09k
    case HB_SCRIPT_MANICHAEAN:
627
2.09k
    case HB_SCRIPT_MENDE_KIKAKUI:
628
2.10k
    case HB_SCRIPT_NABATAEAN:
629
2.10k
    case HB_SCRIPT_OLD_NORTH_ARABIAN:
630
2.11k
    case HB_SCRIPT_PALMYRENE:
631
2.11k
    case HB_SCRIPT_PSALTER_PAHLAVI:
632
633
    /* Unicode-8.0 additions */
634
2.12k
    case HB_SCRIPT_HATRAN:
635
636
    /* Unicode-9.0 additions */
637
2.12k
    case HB_SCRIPT_ADLAM:
638
639
    /* Unicode-11.0 additions */
640
2.14k
    case HB_SCRIPT_HANIFI_ROHINGYA:
641
2.14k
    case HB_SCRIPT_OLD_SOGDIAN:
642
2.15k
    case HB_SCRIPT_SOGDIAN:
643
644
    /* Unicode-12.0 additions */
645
2.16k
    case HB_SCRIPT_ELYMAIC:
646
647
    /* Unicode-13.0 additions */
648
2.16k
    case HB_SCRIPT_CHORASMIAN:
649
2.16k
    case HB_SCRIPT_YEZIDI:
650
651
    /* Unicode-14.0 additions */
652
2.17k
    case HB_SCRIPT_OLD_UYGHUR:
653
654
2.17k
      return HB_DIRECTION_RTL;
655
656
657
    /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
658
5
    case HB_SCRIPT_OLD_HUNGARIAN:
659
11
    case HB_SCRIPT_OLD_ITALIC:
660
17
    case HB_SCRIPT_RUNIC:
661
662
17
      return HB_DIRECTION_INVALID;
663
340k
  }
664
665
338k
  return HB_DIRECTION_LTR;
666
340k
}
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
695
hb_version (unsigned int *major,
696
      unsigned int *minor,
697
      unsigned int *micro)
698
0
{
699
0
  *major = HB_VERSION_MAJOR;
700
0
  *minor = HB_VERSION_MINOR;
701
0
  *micro = HB_VERSION_MICRO;
702
0
}
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 *
714
hb_version_string ()
715
0
{
716
0
  return HB_VERSION_STRING;
717
0
}
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
734
hb_version_atleast (unsigned int major,
735
        unsigned int minor,
736
        unsigned int micro)
737
0
{
738
0
  return HB_VERSION_ATLEAST (major, minor, micro);
739
0
}
740
741
742
743
/* hb_feature_t and hb_variation_t */
744
745
static bool
746
parse_space (const char **pp, const char *end)
747
0
{
748
0
  while (*pp < end && ISSPACE (**pp))
749
0
    (*pp)++;
750
0
  return true;
751
0
}
752
753
static bool
754
parse_char (const char **pp, const char *end, char c)
755
0
{
756
0
  parse_space (pp, end);
757
758
0
  if (*pp == end || **pp != c)
759
0
    return false;
760
761
0
  (*pp)++;
762
0
  return true;
763
0
}
764
765
static bool
766
parse_uint (const char **pp, const char *end, unsigned int *pv)
767
0
{
768
  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
769
   * such that -1 turns into "big number"... */
770
0
  int v;
771
0
  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
772
773
0
  *pv = v;
774
0
  return true;
775
0
}
776
777
static bool
778
parse_uint32 (const char **pp, const char *end, uint32_t *pv)
779
0
{
780
  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
781
   * such that -1 turns into "big number"... */
782
0
  int v;
783
0
  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
784
785
0
  *pv = v;
786
0
  return true;
787
0
}
788
789
static bool
790
parse_bool (const char **pp, const char *end, uint32_t *pv)
791
0
{
792
0
  parse_space (pp, end);
793
794
0
  const char *p = *pp;
795
0
  while (*pp < end && ISALPHA(**pp))
796
0
    (*pp)++;
797
798
  /* CSS allows on/off as aliases 1/0. */
799
0
  if (*pp - p == 2
800
0
      && TOLOWER (p[0]) == 'o'
801
0
      && TOLOWER (p[1]) == 'n')
802
0
    *pv = 1;
803
0
  else if (*pp - p == 3
804
0
     && TOLOWER (p[0]) == 'o'
805
0
     && TOLOWER (p[1]) == 'f'
806
0
     && TOLOWER (p[2]) == 'f')
807
0
    *pv = 0;
808
0
  else
809
0
    return false;
810
811
0
  return true;
812
0
}
813
814
/* hb_feature_t */
815
816
static bool
817
parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
818
0
{
819
0
  if (parse_char (pp, end, '-'))
820
0
    feature->value = 0;
821
0
  else {
822
0
    parse_char (pp, end, '+');
823
0
    feature->value = 1;
824
0
  }
825
826
0
  return true;
827
0
}
828
829
static bool
830
parse_tag (const char **pp, const char *end, hb_tag_t *tag)
831
0
{
832
0
  parse_space (pp, end);
833
834
0
  char quote = 0;
835
836
0
  if (*pp < end && (**pp == '\'' || **pp == '"'))
837
0
  {
838
0
    quote = **pp;
839
0
    (*pp)++;
840
0
  }
841
842
0
  const char *p = *pp;
843
0
  while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
844
0
    (*pp)++;
845
846
0
  if (p == *pp || *pp - p > 4)
847
0
    return false;
848
849
0
  *tag = hb_tag_from_string (p, *pp - p);
850
851
0
  if (quote)
852
0
  {
853
    /* CSS expects exactly four bytes.  And we only allow quotations for
854
     * CSS compatibility.  So, enforce the length. */
855
0
     if (*pp - p != 4)
856
0
       return false;
857
0
    if (*pp == end || **pp != quote)
858
0
      return false;
859
0
    (*pp)++;
860
0
  }
861
862
0
  return true;
863
0
}
864
865
static bool
866
parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
867
0
{
868
0
  parse_space (pp, end);
869
870
0
  bool has_start;
871
872
0
  feature->start = HB_FEATURE_GLOBAL_START;
873
0
  feature->end = HB_FEATURE_GLOBAL_END;
874
875
0
  if (!parse_char (pp, end, '['))
876
0
    return true;
877
878
0
  has_start = parse_uint (pp, end, &feature->start);
879
880
0
  if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
881
0
    parse_uint (pp, end, &feature->end);
882
0
  } else {
883
0
    if (has_start)
884
0
      feature->end = feature->start + 1;
885
0
  }
886
887
0
  return parse_char (pp, end, ']');
888
0
}
889
890
static bool
891
parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
892
0
{
893
0
  bool had_equal = parse_char (pp, end, '=');
894
0
  bool had_value = parse_uint32 (pp, end, &feature->value) ||
895
0
       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
0
  return !had_equal || had_value;
900
0
}
901
902
static bool
903
parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
904
0
{
905
0
  return parse_feature_value_prefix (pp, end, feature) &&
906
0
   parse_tag (pp, end, &feature->tag) &&
907
0
   parse_feature_indices (pp, end, feature) &&
908
0
   parse_feature_value_postfix (pp, end, feature) &&
909
0
   parse_space (pp, end) &&
910
0
   *pp == end;
911
0
}
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
963
hb_feature_from_string (const char *str, int len,
964
      hb_feature_t *feature)
965
0
{
966
0
  hb_feature_t feat;
967
968
0
  if (len < 0)
969
0
    len = strlen (str);
970
971
0
  if (likely (parse_one_feature (&str, str + len, &feat)))
972
0
  {
973
0
    if (feature)
974
0
      *feature = feat;
975
0
    return true;
976
0
  }
977
978
0
  if (feature)
979
0
    hb_memset (feature, 0, sizeof (*feature));
980
0
  return false;
981
0
}
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
996
hb_feature_to_string (hb_feature_t *feature,
997
          char *buf, unsigned int size)
998
0
{
999
0
  if (unlikely (!size)) return;
1000
1001
0
  char s[128];
1002
0
  unsigned int len = 0;
1003
0
  if (feature->value == 0)
1004
0
    s[len++] = '-';
1005
0
  hb_tag_to_string (feature->tag, s + len);
1006
0
  len += 4;
1007
0
  while (len && s[len - 1] == ' ')
1008
0
    len--;
1009
0
  if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
1010
0
  {
1011
0
    s[len++] = '[';
1012
0
    if (feature->start)
1013
0
      len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
1014
0
    if (feature->end != feature->start + 1) {
1015
0
      s[len++] = ':';
1016
0
      if (feature->end != HB_FEATURE_GLOBAL_END)
1017
0
  len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
1018
0
    }
1019
0
    s[len++] = ']';
1020
0
  }
1021
0
  if (feature->value > 1)
1022
0
  {
1023
0
    s[len++] = '=';
1024
0
    len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
1025
0
  }
1026
0
  assert (len < ARRAY_LENGTH (s));
1027
0
  len = hb_min (len, size - 1);
1028
0
  hb_memcpy (buf, s, len);
1029
0
  buf[len] = '\0';
1030
0
}
1031
1032
/* hb_variation_t */
1033
1034
static bool
1035
parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
1036
0
{
1037
0
  parse_char (pp, end, '='); /* Optional. */
1038
0
  double v;
1039
0
  if (unlikely (!hb_parse_double (pp, end, &v))) return false;
1040
1041
0
  variation->value = v;
1042
0
  return true;
1043
0
}
1044
1045
static bool
1046
parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
1047
0
{
1048
0
  return parse_tag (pp, end, &variation->tag) &&
1049
0
   parse_variation_value (pp, end, variation) &&
1050
0
   parse_space (pp, end) &&
1051
0
   *pp == end;
1052
0
}
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
1075
hb_variation_from_string (const char *str, int len,
1076
        hb_variation_t *variation)
1077
0
{
1078
0
  hb_variation_t var;
1079
1080
0
  if (len < 0)
1081
0
    len = strlen (str);
1082
1083
0
  if (likely (parse_one_variation (&str, str + len, &var)))
1084
0
  {
1085
0
    if (variation)
1086
0
      *variation = var;
1087
0
    return true;
1088
0
  }
1089
1090
0
  if (variation)
1091
0
    hb_memset (variation, 0, sizeof (*variation));
1092
0
  return false;
1093
0
}
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
{
1102
  static hb_locale_t create ()
1103
0
  {
1104
0
    hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
1105
0
    if (!l)
1106
0
      return l;
1107
1108
0
    hb_atexit (free_static_C_locale);
1109
1110
0
    return l;
1111
0
  }
1112
  static void destroy (hb_locale_t l)
1113
0
  {
1114
0
    freelocale (l);
1115
0
  }
1116
  static hb_locale_t get_null ()
1117
0
  {
1118
0
    return (hb_locale_t) 0;
1119
0
  }
1120
} static_C_locale;
1121
1122
static inline
1123
void free_static_C_locale ()
1124
0
{
1125
0
  static_C_locale.free_instance ();
1126
0
}
1127
1128
static hb_locale_t
1129
get_C_locale ()
1130
0
{
1131
0
  return static_C_locale.get_unconst ();
1132
0
}
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
1149
hb_variation_to_string (hb_variation_t *variation,
1150
      char *buf, unsigned int size)
1151
0
{
1152
0
  if (unlikely (!size)) return;
1153
1154
0
  char s[128];
1155
0
  unsigned int len = 0;
1156
0
  hb_tag_to_string (variation->tag, s + len);
1157
0
  len += 4;
1158
0
  while (len && s[len - 1] == ' ')
1159
0
    len--;
1160
0
  s[len++] = '=';
1161
1162
0
  hb_locale_t oldlocale HB_UNUSED;
1163
0
  oldlocale = hb_uselocale (get_C_locale ());
1164
0
  len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
1165
0
  (void) hb_uselocale (oldlocale);
1166
1167
0
  assert (len < ARRAY_LENGTH (s));
1168
0
  len = hb_min (len, size - 1);
1169
0
  hb_memcpy (buf, s, len);
1170
0
  buf[len] = '\0';
1171
0
}
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
 */
1183
uint8_t
1184
(hb_color_get_alpha) (hb_color_t color)
1185
0
{
1186
0
  return hb_color_get_alpha (color);
1187
0
}
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
 */
1199
uint8_t
1200
(hb_color_get_red) (hb_color_t color)
1201
0
{
1202
0
  return hb_color_get_red (color);
1203
0
}
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
 */
1215
uint8_t
1216
(hb_color_get_green) (hb_color_t color)
1217
0
{
1218
0
  return hb_color_get_green (color);
1219
0
}
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
 */
1231
uint8_t
1232
(hb_color_get_blue) (hb_color_t color)
1233
0
{
1234
0
  return hb_color_get_blue (color);
1235
0
}
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