Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/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
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
33.1M
{
99
33.1M
  char tag[4];
100
33.1M
  unsigned int i;
101
102
33.1M
  if (!str || !len || !*str)
103
18
    return HB_TAG_NONE;
104
105
33.1M
  if (len < 0 || len > 4)
106
32.5M
    len = 4;
107
164M
  for (i = 0; i < (unsigned) len && str[i]; i++)
108
131M
    tag[i] = str[i];
109
34.3M
  for (; i < 4; i++)
110
1.21M
    tag[i] = ' ';
111
112
33.1M
  return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
113
33.1M
}
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
188M
{
219
188M
  const unsigned char *p1 = (const unsigned char *) v1;
220
188M
  const unsigned char *p2 = (const unsigned char *) v2;
221
222
385M
  while (*p1 && *p1 == canon_map[*p2]) {
223
196M
    p1++;
224
196M
    p2++;
225
196M
  }
226
227
188M
  return *p1 == canon_map[*p2];
228
188M
}
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
188M
  { return lang_equal (lang, s); }
254
255
  hb_language_item_t & operator = (const char *s)
256
161
  {
257
    /* We can't call strdup(), because we allow custom allocators. */
258
161
    size_t len = strlen(s) + 1;
259
161
    lang = (hb_language_t) hb_malloc(len);
260
161
    if (likely (lang))
261
161
    {
262
161
      hb_memcpy((unsigned char *) lang, s, len);
263
907
      for (unsigned char *p = (unsigned char *) lang; *p; p++)
264
746
  *p = canon_map[*p];
265
161
    }
266
267
161
    return *this;
268
161
  }
269
270
161
  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
28
{
281
28
retry:
282
28
  hb_language_item_t *first_lang = langs;
283
28
  if (unlikely (!langs.cmpexch (first_lang, nullptr)))
284
0
    goto retry;
285
286
189
  while (first_lang) {
287
161
    hb_language_item_t *next = first_lang->next;
288
161
    first_lang->fini ();
289
161
    hb_free (first_lang);
290
161
    first_lang = next;
291
161
  }
292
28
}
293
294
static hb_language_item_t *
295
lang_find_or_insert (const char *key)
296
32.8M
{
297
32.8M
retry:
298
32.8M
  hb_language_item_t *first_lang = langs;
299
300
188M
  for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
301
188M
    if (*lang == key)
302
32.8M
      return lang;
303
304
  /* Not found; allocate one. */
305
161
  hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));
306
161
  if (unlikely (!lang))
307
0
    return nullptr;
308
161
  lang->next = first_lang;
309
161
  *lang = key;
310
161
  if (unlikely (!lang->lang))
311
0
  {
312
0
    hb_free (lang);
313
0
    return nullptr;
314
0
  }
315
316
161
  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
161
  if (!first_lang)
324
28
    hb_atexit (free_langs); /* First person registers atexit() callback. */
325
326
161
  return lang;
327
161
}
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
32.8M
{
347
32.8M
  if (!str || !len || !*str)
348
0
    return HB_LANGUAGE_INVALID;
349
350
32.8M
  hb_language_item_t *item = nullptr;
351
32.8M
  if (len >= 0)
352
32.8M
  {
353
    /* NUL-terminate it. */
354
32.8M
    char strbuf[64];
355
32.8M
    len = hb_min (len, (int) sizeof (strbuf) - 1);
356
32.8M
    hb_memcpy (strbuf, str, len);
357
32.8M
    strbuf[len] = '\0';
358
32.8M
    item = lang_find_or_insert (strbuf);
359
32.8M
  }
360
270
  else
361
270
    item = lang_find_or_insert (str);
362
363
32.8M
  return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
364
32.8M
}
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
8.85k
{
381
8.85k
  if (unlikely (!language)) return nullptr;
382
383
8.85k
  return language->s;
384
8.85k
}
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
32.5M
{
467
32.5M
  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
32.5M
  tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
472
473
32.5M
  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
32.5M
  }
494
495
  /* If it looks right, just use the tag as a script */
496
32.5M
  if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
497
32.5M
    return (hb_script_t) tag;
498
499
  /* Otherwise, return unknown */
500
0
  return HB_SCRIPT_UNKNOWN;
501
32.5M
}
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
32.5M
{
521
32.5M
  return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
522
32.5M
}
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.  Scripts that can be written either
549
 * horizontally or vertically will return #HB_DIRECTION_INVALID.
550
 * Unknown scripts will return #HB_DIRECTION_LTR.
551
 *
552
 * Return value: The horizontal #hb_direction_t of @script
553
 *
554
 * Since: 0.9.2
555
 **/
556
hb_direction_t
557
hb_script_get_horizontal_direction (hb_script_t script)
558
32.9M
{
559
  /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
560
32.9M
  switch ((hb_tag_t) script)
561
32.9M
  {
562
    /* Unicode-1.1 additions */
563
552k
    case HB_SCRIPT_ARABIC:
564
905k
    case HB_SCRIPT_HEBREW:
565
566
    /* Unicode-3.0 additions */
567
1.11M
    case HB_SCRIPT_SYRIAC:
568
1.13M
    case HB_SCRIPT_THAANA:
569
570
    /* Unicode-4.0 additions */
571
1.13M
    case HB_SCRIPT_CYPRIOT:
572
573
    /* Unicode-4.1 additions */
574
1.13M
    case HB_SCRIPT_KHAROSHTHI:
575
576
    /* Unicode-5.0 additions */
577
1.13M
    case HB_SCRIPT_PHOENICIAN:
578
1.15M
    case HB_SCRIPT_NKO:
579
580
    /* Unicode-5.1 additions */
581
1.15M
    case HB_SCRIPT_LYDIAN:
582
583
    /* Unicode-5.2 additions */
584
1.15M
    case HB_SCRIPT_AVESTAN:
585
1.15M
    case HB_SCRIPT_IMPERIAL_ARAMAIC:
586
1.15M
    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
587
1.15M
    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
588
1.15M
    case HB_SCRIPT_OLD_SOUTH_ARABIAN:
589
1.15M
    case HB_SCRIPT_OLD_TURKIC:
590
1.30M
    case HB_SCRIPT_SAMARITAN:
591
592
    /* Unicode-6.0 additions */
593
1.30M
    case HB_SCRIPT_MANDAIC:
594
595
    /* Unicode-6.1 additions */
596
1.30M
    case HB_SCRIPT_MEROITIC_CURSIVE:
597
1.30M
    case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
598
599
    /* Unicode-7.0 additions */
600
1.30M
    case HB_SCRIPT_MANICHAEAN:
601
1.31M
    case HB_SCRIPT_MENDE_KIKAKUI:
602
1.31M
    case HB_SCRIPT_NABATAEAN:
603
1.31M
    case HB_SCRIPT_OLD_NORTH_ARABIAN:
604
1.31M
    case HB_SCRIPT_PALMYRENE:
605
1.31M
    case HB_SCRIPT_PSALTER_PAHLAVI:
606
607
    /* Unicode-8.0 additions */
608
1.31M
    case HB_SCRIPT_HATRAN:
609
610
    /* Unicode-9.0 additions */
611
1.32M
    case HB_SCRIPT_ADLAM:
612
613
    /* Unicode-11.0 additions */
614
1.32M
    case HB_SCRIPT_HANIFI_ROHINGYA:
615
1.32M
    case HB_SCRIPT_OLD_SOGDIAN:
616
1.32M
    case HB_SCRIPT_SOGDIAN:
617
618
    /* Unicode-12.0 additions */
619
1.32M
    case HB_SCRIPT_ELYMAIC:
620
621
    /* Unicode-13.0 additions */
622
1.32M
    case HB_SCRIPT_CHORASMIAN:
623
1.32M
    case HB_SCRIPT_YEZIDI:
624
625
    /* Unicode-14.0 additions */
626
1.32M
    case HB_SCRIPT_OLD_UYGHUR:
627
628
    /* Unicode-16.0 additions */
629
1.32M
    case HB_SCRIPT_GARAY:
630
631
1.32M
      return HB_DIRECTION_RTL;
632
633
634
    /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
635
0
    case HB_SCRIPT_OLD_HUNGARIAN:
636
88
    case HB_SCRIPT_OLD_ITALIC:
637
8.12k
    case HB_SCRIPT_RUNIC:
638
22.3k
    case HB_SCRIPT_TIFINAGH:
639
640
22.3k
      return HB_DIRECTION_INVALID;
641
32.9M
  }
642
643
31.5M
  return HB_DIRECTION_LTR;
644
32.9M
}
645
646
647
/* hb_version */
648
649
650
/**
651
 * SECTION:hb-version
652
 * @title: hb-version
653
 * @short_description: Information about the version of HarfBuzz in use
654
 * @include: hb.h
655
 *
656
 * These functions and macros allow accessing version of the HarfBuzz
657
 * library used at compile- as well as run-time, and to direct code
658
 * conditionally based on those versions, again, at compile- or run-time.
659
 **/
660
661
662
/**
663
 * hb_version:
664
 * @major: (out): Library major version component
665
 * @minor: (out): Library minor version component
666
 * @micro: (out): Library micro version component
667
 *
668
 * Returns library version as three integer components.
669
 *
670
 * Since: 0.9.2
671
 **/
672
void
673
hb_version (unsigned int *major,
674
      unsigned int *minor,
675
      unsigned int *micro)
676
0
{
677
0
  *major = HB_VERSION_MAJOR;
678
0
  *minor = HB_VERSION_MINOR;
679
0
  *micro = HB_VERSION_MICRO;
680
0
}
681
682
/**
683
 * hb_version_string:
684
 *
685
 * Returns library version as a string with three components.
686
 *
687
 * Return value: Library version string
688
 *
689
 * Since: 0.9.2
690
 **/
691
const char *
692
hb_version_string ()
693
0
{
694
0
  return HB_VERSION_STRING;
695
0
}
696
697
/**
698
 * hb_version_atleast:
699
 * @major: Library major version component
700
 * @minor: Library minor version component
701
 * @micro: Library micro version component
702
 *
703
 * Tests the library version against a minimum value,
704
 * as three integer components.
705
 *
706
 * Return value: `true` if the library is equal to or greater than
707
 * the test value, `false` otherwise
708
 *
709
 * Since: 0.9.30
710
 **/
711
hb_bool_t
712
hb_version_atleast (unsigned int major,
713
        unsigned int minor,
714
        unsigned int micro)
715
0
{
716
0
  return HB_VERSION_ATLEAST (major, minor, micro);
717
0
}
718
719
720
721
/* hb_feature_t and hb_variation_t */
722
723
static bool
724
parse_space (const char **pp, const char *end)
725
4.68M
{
726
4.80M
  while (*pp < end && ISSPACE (**pp))
727
117k
    (*pp)++;
728
4.68M
  return true;
729
4.68M
}
730
731
static bool
732
parse_char (const char **pp, const char *end, char c)
733
2.46M
{
734
2.46M
  parse_space (pp, end);
735
736
2.46M
  if (*pp == end || **pp != c)
737
2.39M
    return false;
738
739
68.1k
  (*pp)++;
740
68.1k
  return true;
741
2.46M
}
742
743
static bool
744
parse_uint (const char **pp, const char *end, unsigned int *pv)
745
34.5k
{
746
  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
747
   * such that -1 turns into "big number"... */
748
34.5k
  int v;
749
34.5k
  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
750
751
16.1k
  *pv = v;
752
16.1k
  return true;
753
34.5k
}
754
755
static bool
756
parse_uint32 (const char **pp, const char *end, uint32_t *pv)
757
518k
{
758
  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
759
   * such that -1 turns into "big number"... */
760
518k
  int v;
761
518k
  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
762
763
5.97k
  *pv = v;
764
5.97k
  return true;
765
518k
}
766
767
static bool
768
parse_bool (const char **pp, const char *end, uint32_t *pv)
769
512k
{
770
512k
  parse_space (pp, end);
771
772
512k
  const char *p = *pp;
773
557k
  while (*pp < end && ISALPHA(**pp))
774
45.4k
    (*pp)++;
775
776
  /* CSS allows on/off as aliases 1/0. */
777
512k
  if (*pp - p == 2
778
512k
      && TOLOWER (p[0]) == 'o'
779
512k
      && TOLOWER (p[1]) == 'n')
780
791
    *pv = 1;
781
511k
  else if (*pp - p == 3
782
511k
     && TOLOWER (p[0]) == 'o'
783
511k
     && TOLOWER (p[1]) == 'f'
784
511k
     && TOLOWER (p[2]) == 'f')
785
498
    *pv = 0;
786
510k
  else
787
510k
    return false;
788
789
1.28k
  return true;
790
512k
}
791
792
/* hb_feature_t */
793
794
static bool
795
parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
796
678k
{
797
678k
  if (parse_char (pp, end, '-'))
798
6.02k
    feature->value = 0;
799
672k
  else {
800
672k
    parse_char (pp, end, '+');
801
672k
    feature->value = 1;
802
672k
  }
803
804
678k
  return true;
805
678k
}
806
807
static bool
808
parse_tag (const char **pp, const char *end, hb_tag_t *tag)
809
678k
{
810
678k
  parse_space (pp, end);
811
812
678k
  char quote = 0;
813
814
678k
  if (*pp < end && (**pp == '\'' || **pp == '"'))
815
7.45k
  {
816
7.45k
    quote = **pp;
817
7.45k
    (*pp)++;
818
7.45k
  }
819
820
678k
  const char *p = *pp;
821
2.27M
  while (*pp < end && (**pp != ' ' && **pp != '=' && **pp != '[' && **pp != quote))
822
1.59M
    (*pp)++;
823
824
678k
  if (p == *pp || *pp - p > 4)
825
149k
    return false;
826
827
528k
  *tag = hb_tag_from_string (p, *pp - p);
828
829
528k
  if (quote)
830
5.86k
  {
831
    /* CSS expects exactly four bytes.  And we only allow quotations for
832
     * CSS compatibility.  So, enforce the length. */
833
5.86k
     if (*pp - p != 4)
834
4.38k
       return false;
835
1.47k
    if (*pp == end || **pp != quote)
836
956
      return false;
837
523
    (*pp)++;
838
523
  }
839
840
523k
  return true;
841
528k
}
842
843
static bool
844
parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
845
523k
{
846
523k
  parse_space (pp, end);
847
848
523k
  bool has_start;
849
850
523k
  feature->start = HB_FEATURE_GLOBAL_START;
851
523k
  feature->end = HB_FEATURE_GLOBAL_END;
852
853
523k
  if (!parse_char (pp, end, '['))
854
497k
    return true;
855
856
25.4k
  has_start = parse_uint (pp, end, &feature->start);
857
858
25.4k
  if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
859
9.04k
    parse_uint (pp, end, &feature->end);
860
16.4k
  } else {
861
16.4k
    if (has_start)
862
9.53k
      feature->end = feature->start + 1;
863
16.4k
  }
864
865
25.4k
  return parse_char (pp, end, ']');
866
523k
}
867
868
static bool
869
parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
870
518k
{
871
518k
  bool had_equal = parse_char (pp, end, '=');
872
518k
  bool had_value = parse_uint32 (pp, end, &feature->value) ||
873
518k
       parse_bool (pp, end, &feature->value);
874
  /* CSS doesn't use equal-sign between tag and value.
875
   * If there was an equal-sign, then there *must* be a value.
876
   * A value without an equal-sign is ok, but not required. */
877
518k
  return !had_equal || had_value;
878
518k
}
879
880
static bool
881
parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
882
678k
{
883
678k
  return parse_feature_value_prefix (pp, end, feature) &&
884
678k
   parse_tag (pp, end, &feature->tag) &&
885
678k
   parse_feature_indices (pp, end, feature) &&
886
678k
   parse_feature_value_postfix (pp, end, feature) &&
887
678k
   parse_space (pp, end) &&
888
678k
   *pp == end;
889
678k
}
890
891
/**
892
 * hb_feature_from_string:
893
 * @str: (array length=len) (element-type uint8_t): a string to parse
894
 * @len: length of @str, or -1 if string is `NULL` terminated
895
 * @feature: (out): the #hb_feature_t to initialize with the parsed values
896
 *
897
 * Parses a string into a #hb_feature_t.
898
 *
899
 * The format for specifying feature strings follows. All valid CSS
900
 * font-feature-settings values other than 'normal' and the global values are
901
 * also accepted, though not documented below. CSS string escapes are not
902
 * supported.
903
 *
904
 * The range indices refer to the positions between Unicode characters. The
905
 * position before the first character is always 0.
906
 *
907
 * The format is Python-esque.  Here is how it all works:
908
 *
909
 * <informaltable pgwide='1' align='left' frame='none'>
910
 * <tgroup cols='5'>
911
 * <thead>
912
 * <row><entry>Syntax</entry>    <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
913
 * </thead>
914
 * <tbody>
915
 * <row><entry>Setting value:</entry></row>
916
 * <row><entry>kern</entry>      <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
917
 * <row><entry>+kern</entry>     <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
918
 * <row><entry>-kern</entry>     <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
919
 * <row><entry>kern=0</entry>    <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
920
 * <row><entry>kern=1</entry>    <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
921
 * <row><entry>aalt=2</entry>    <entry>2</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Choose 2nd alternate</entry></row>
922
 * <row><entry>Setting index:</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>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
925
 * <row><entry>kern[5:]</entry>  <entry>1</entry>     <entry>5</entry>      <entry>∞</entry>   <entry>Turn feature on, partial</entry></row>
926
 * <row><entry>kern[:5]</entry>  <entry>1</entry>     <entry>0</entry>      <entry>5</entry>   <entry>Turn feature on, partial</entry></row>
927
 * <row><entry>kern[3:5]</entry> <entry>1</entry>     <entry>3</entry>      <entry>5</entry>   <entry>Turn feature on, range</entry></row>
928
 * <row><entry>kern[3]</entry>   <entry>1</entry>     <entry>3</entry>      <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
929
 * <row><entry>Mixing it all:</entry></row>
930
 * <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>
931
 * </tbody>
932
 * </tgroup>
933
 * </informaltable>
934
 *
935
 * Return value:
936
 * `true` if @str is successfully parsed, `false` otherwise
937
 *
938
 * Since: 0.9.5
939
 **/
940
hb_bool_t
941
hb_feature_from_string (const char *str, int len,
942
      hb_feature_t *feature)
943
678k
{
944
678k
  hb_feature_t feat;
945
946
678k
  if (len < 0)
947
0
    len = strlen (str);
948
949
678k
  if (likely (parse_one_feature (&str, str + len, &feat)))
950
418k
  {
951
418k
    if (feature)
952
418k
      *feature = feat;
953
418k
    return true;
954
418k
  }
955
956
259k
  if (feature)
957
259k
    hb_memset (feature, 0, sizeof (*feature));
958
259k
  return false;
959
678k
}
960
961
/**
962
 * hb_feature_to_string:
963
 * @feature: an #hb_feature_t to convert
964
 * @buf: (array length=size) (out): output string
965
 * @size: the allocated size of @buf
966
 *
967
 * Converts a #hb_feature_t into a `NULL`-terminated string in the format
968
 * understood by hb_feature_from_string(). The client in responsible for
969
 * allocating big enough size for @buf, 128 bytes is more than enough.
970
 *
971
 * Note that the feature value will be omitted if it is '1', but the
972
 * string won't include any whitespace.
973
 *
974
 * Since: 0.9.5
975
 **/
976
void
977
hb_feature_to_string (hb_feature_t *feature,
978
          char *buf, unsigned int size)
979
0
{
980
0
  if (unlikely (!size)) return;
981
982
0
  char s[128];
983
0
  unsigned int len = 0;
984
0
  if (feature->value == 0)
985
0
    s[len++] = '-';
986
0
  hb_tag_to_string (feature->tag, s + len);
987
0
  len += 4;
988
0
  while (len && s[len - 1] == ' ')
989
0
    len--;
990
0
  if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
991
0
  {
992
0
    s[len++] = '[';
993
0
    if (feature->start)
994
0
      len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
995
0
    if (feature->end != feature->start + 1) {
996
0
      s[len++] = ':';
997
0
      if (feature->end != HB_FEATURE_GLOBAL_END)
998
0
  len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
999
0
    }
1000
0
    s[len++] = ']';
1001
0
  }
1002
0
  if (feature->value > 1)
1003
0
  {
1004
0
    s[len++] = '=';
1005
0
    len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%" PRIu32, feature->value));
1006
0
  }
1007
0
  assert (len < ARRAY_LENGTH (s));
1008
0
  len = hb_min (len, size - 1);
1009
0
  hb_memcpy (buf, s, len);
1010
0
  buf[len] = '\0';
1011
0
}
1012
1013
/* hb_variation_t */
1014
1015
static bool
1016
parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
1017
0
{
1018
0
  parse_char (pp, end, '='); /* Optional. */
1019
0
  double v;
1020
0
  if (unlikely (!hb_parse_double (pp, end, &v))) return false;
1021
1022
0
  variation->value = v;
1023
0
  return true;
1024
0
}
1025
1026
static bool
1027
parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
1028
0
{
1029
0
  return parse_tag (pp, end, &variation->tag) &&
1030
0
   parse_variation_value (pp, end, variation) &&
1031
0
   parse_space (pp, end) &&
1032
0
   *pp == end;
1033
0
}
1034
1035
/**
1036
 * hb_variation_from_string:
1037
 * @str: (array length=len) (element-type uint8_t): a string to parse
1038
 * @len: length of @str, or -1 if string is `NULL` terminated
1039
 * @variation: (out): the #hb_variation_t to initialize with the parsed values
1040
 *
1041
 * Parses a string into a #hb_variation_t.
1042
 *
1043
 * The format for specifying variation settings follows. All valid CSS
1044
 * font-variation-settings values other than 'normal' and 'inherited' are also
1045
 * accepted, though, not documented below.
1046
 *
1047
 * The format is a tag, optionally followed by an equals sign, followed by a
1048
 * number. For example `wght=500`, or `slnt=-7.5`.
1049
 *
1050
 * Return value:
1051
 * `true` if @str is successfully parsed, `false` otherwise
1052
 *
1053
 * Since: 1.4.2
1054
 */
1055
hb_bool_t
1056
hb_variation_from_string (const char *str, int len,
1057
        hb_variation_t *variation)
1058
0
{
1059
0
  hb_variation_t var;
1060
1061
0
  if (len < 0)
1062
0
    len = strlen (str);
1063
1064
0
  if (likely (parse_one_variation (&str, str + len, &var)))
1065
0
  {
1066
0
    if (variation)
1067
0
      *variation = var;
1068
0
    return true;
1069
0
  }
1070
1071
0
  if (variation)
1072
0
    hb_memset (variation, 0, sizeof (*variation));
1073
0
  return false;
1074
0
}
1075
1076
#ifndef HB_NO_SETLOCALE
1077
1078
static inline void free_static_C_locale ();
1079
1080
static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,
1081
                 hb_C_locale_lazy_loader_t>
1082
{
1083
  static hb_locale_t create ()
1084
0
  {
1085
0
    hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
1086
0
    if (!l)
1087
0
      return l;
1088
1089
0
    hb_atexit (free_static_C_locale);
1090
1091
0
    return l;
1092
0
  }
1093
  static void destroy (hb_locale_t l)
1094
0
  {
1095
0
    freelocale (l);
1096
0
  }
1097
  static hb_locale_t get_null ()
1098
0
  {
1099
0
    return (hb_locale_t) 0;
1100
0
  }
1101
} static_C_locale;
1102
1103
static inline
1104
void free_static_C_locale ()
1105
0
{
1106
0
  static_C_locale.free_instance ();
1107
0
}
1108
1109
static hb_locale_t
1110
get_C_locale ()
1111
0
{
1112
0
  return static_C_locale.get_unconst ();
1113
0
}
1114
1115
#endif
1116
1117
/**
1118
 * hb_variation_to_string:
1119
 * @variation: an #hb_variation_t to convert
1120
 * @buf: (array length=size) (out caller-allocates): output string
1121
 * @size: the allocated size of @buf
1122
 *
1123
 * Converts an #hb_variation_t into a `NULL`-terminated string in the format
1124
 * understood by hb_variation_from_string(). The client in responsible for
1125
 * allocating big enough size for @buf, 128 bytes is more than enough.
1126
 *
1127
 * Note that the string won't include any whitespace.
1128
 *
1129
 * Since: 1.4.2
1130
 */
1131
void
1132
hb_variation_to_string (hb_variation_t *variation,
1133
      char *buf, unsigned int size)
1134
0
{
1135
0
  if (unlikely (!size)) return;
1136
1137
0
  char s[128];
1138
0
  unsigned int len = 0;
1139
0
  hb_tag_to_string (variation->tag, s + len);
1140
0
  len += 4;
1141
0
  while (len && s[len - 1] == ' ')
1142
0
    len--;
1143
0
  s[len++] = '=';
1144
1145
0
  hb_locale_t oldlocale HB_UNUSED;
1146
0
  oldlocale = hb_uselocale (get_C_locale ());
1147
0
  len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
1148
0
  (void) hb_uselocale (oldlocale);
1149
1150
0
  assert (len < ARRAY_LENGTH (s));
1151
0
  len = hb_min (len, size - 1);
1152
0
  hb_memcpy (buf, s, len);
1153
0
  buf[len] = '\0';
1154
0
}
1155
1156
/**
1157
 * hb_color_get_alpha:
1158
 * @color: an #hb_color_t we are interested in its channels.
1159
 *
1160
 * Fetches the alpha channel of the given @color.
1161
 *
1162
 * Return value: Alpha channel value
1163
 *
1164
 * Since: 2.1.0
1165
 */
1166
uint8_t
1167
(hb_color_get_alpha) (hb_color_t color)
1168
0
{
1169
0
  return hb_color_get_alpha (color);
1170
0
}
1171
1172
/**
1173
 * hb_color_get_red:
1174
 * @color: an #hb_color_t we are interested in its channels.
1175
 *
1176
 * Fetches the red channel of the given @color.
1177
 *
1178
 * Return value: Red channel value
1179
 *
1180
 * Since: 2.1.0
1181
 */
1182
uint8_t
1183
(hb_color_get_red) (hb_color_t color)
1184
0
{
1185
0
  return hb_color_get_red (color);
1186
0
}
1187
1188
/**
1189
 * hb_color_get_green:
1190
 * @color: an #hb_color_t we are interested in its channels.
1191
 *
1192
 * Fetches the green channel of the given @color.
1193
 *
1194
 * Return value: Green channel value
1195
 *
1196
 * Since: 2.1.0
1197
 */
1198
uint8_t
1199
(hb_color_get_green) (hb_color_t color)
1200
0
{
1201
0
  return hb_color_get_green (color);
1202
0
}
1203
1204
/**
1205
 * hb_color_get_blue:
1206
 * @color: an #hb_color_t we are interested in its channels.
1207
 *
1208
 * Fetches the blue channel of the given @color.
1209
 *
1210
 * Return value: Blue channel value
1211
 *
1212
 * Since: 2.1.0
1213
 */
1214
uint8_t
1215
(hb_color_get_blue) (hb_color_t color)
1216
0
{
1217
0
  return hb_color_get_blue (color);
1218
0
}
1219
1220
/**
1221
 * hb_malloc:
1222
 * @size: The size of the memory to allocate.
1223
 *
1224
 * Allocates @size bytes of memory, using the allocator set at
1225
 * compile-time. Typically just malloc().
1226
 *
1227
 * Return value: A pointer to the allocated memory.
1228
 *
1229
 * Since: 11.0.0
1230
 **/
1231
18.0k
void* hb_malloc(size_t size) { return hb_malloc_impl (size); }
1232
1233
/**
1234
 * hb_calloc:
1235
 * @nmemb: The number of elements to allocate.
1236
 * @size: The size of each element.
1237
 *
1238
 * Allocates @nmemb elements of @size bytes each, initialized to zero,
1239
 * using the allocator set at compile-time. Typically just calloc().
1240
 *
1241
 * Return value: A pointer to the allocated memory.
1242
 *
1243
 * Since: 11.0.0
1244
 **/
1245
38.1M
void* hb_calloc(size_t nmemb, size_t size) { return hb_calloc_impl (nmemb, size); }
1246
1247
/**
1248
 * hb_realloc:
1249
 * @ptr: The pointer to the memory to reallocate.
1250
 * @size: The new size of the memory.
1251
 *
1252
 * Reallocates the memory pointed to by @ptr to @size bytes, using the
1253
 * allocator set at compile-time. Typically just realloc().
1254
 *
1255
 * Return value: A pointer to the reallocated memory.
1256
 *
1257
 * Since: 11.0.0
1258
 **/
1259
33.9M
void* hb_realloc(void *ptr, size_t size) { return hb_realloc_impl (ptr, size); }
1260
1261
/**
1262
 * hb_free:
1263
 * @ptr: The pointer to the memory to free.
1264
 *
1265
 * Frees the memory pointed to by @ptr, using the allocator set at
1266
 * compile-time. Typically just free().
1267
 *
1268
 * Since: 11.0.0
1269
 **/
1270
72.5M
void  hb_free(void *ptr) { hb_free_impl (ptr); }
1271
1272
1273
/* If there is no visibility control, then hb-static.cc will NOT
1274
 * define anything.  Instead, we get it to define one set in here
1275
 * only, so only libharfbuzz.so defines them, not other libs. */
1276
#ifdef HB_NO_VISIBILITY
1277
#undef HB_NO_VISIBILITY
1278
#include "hb-static.cc"
1279
#define HB_NO_VISIBILITY 1
1280
#endif