Coverage Report

Created: 2025-12-08 09:28

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