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