Coverage Report

Created: 2026-03-31 11:00

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