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