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