jdk

Форк
0
1220 строк · 30.8 Кб
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_int_t _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_ptr_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_ptr_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.  Scripts that can be written either
549
 * horizontally or vertically will return #HB_DIRECTION_INVALID.
550
 * Unknown scripts will return #HB_DIRECTION_LTR.
551
 *
552
 * Return value: The horizontal #hb_direction_t of @script
553
 *
554
 * Since: 0.9.2
555
 **/
556
hb_direction_t
557
hb_script_get_horizontal_direction (hb_script_t script)
558
{
559
  /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
560
  switch ((hb_tag_t) script)
561
  {
562
    /* Unicode-1.1 additions */
563
    case HB_SCRIPT_ARABIC:
564
    case HB_SCRIPT_HEBREW:
565

566
    /* Unicode-3.0 additions */
567
    case HB_SCRIPT_SYRIAC:
568
    case HB_SCRIPT_THAANA:
569

570
    /* Unicode-4.0 additions */
571
    case HB_SCRIPT_CYPRIOT:
572

573
    /* Unicode-4.1 additions */
574
    case HB_SCRIPT_KHAROSHTHI:
575

576
    /* Unicode-5.0 additions */
577
    case HB_SCRIPT_PHOENICIAN:
578
    case HB_SCRIPT_NKO:
579

580
    /* Unicode-5.1 additions */
581
    case HB_SCRIPT_LYDIAN:
582

583
    /* Unicode-5.2 additions */
584
    case HB_SCRIPT_AVESTAN:
585
    case HB_SCRIPT_IMPERIAL_ARAMAIC:
586
    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
587
    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
588
    case HB_SCRIPT_OLD_SOUTH_ARABIAN:
589
    case HB_SCRIPT_OLD_TURKIC:
590
    case HB_SCRIPT_SAMARITAN:
591

592
    /* Unicode-6.0 additions */
593
    case HB_SCRIPT_MANDAIC:
594

595
    /* Unicode-6.1 additions */
596
    case HB_SCRIPT_MEROITIC_CURSIVE:
597
    case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
598

599
    /* Unicode-7.0 additions */
600
    case HB_SCRIPT_MANICHAEAN:
601
    case HB_SCRIPT_MENDE_KIKAKUI:
602
    case HB_SCRIPT_NABATAEAN:
603
    case HB_SCRIPT_OLD_NORTH_ARABIAN:
604
    case HB_SCRIPT_PALMYRENE:
605
    case HB_SCRIPT_PSALTER_PAHLAVI:
606

607
    /* Unicode-8.0 additions */
608
    case HB_SCRIPT_HATRAN:
609

610
    /* Unicode-9.0 additions */
611
    case HB_SCRIPT_ADLAM:
612

613
    /* Unicode-11.0 additions */
614
    case HB_SCRIPT_HANIFI_ROHINGYA:
615
    case HB_SCRIPT_OLD_SOGDIAN:
616
    case HB_SCRIPT_SOGDIAN:
617

618
    /* Unicode-12.0 additions */
619
    case HB_SCRIPT_ELYMAIC:
620

621
    /* Unicode-13.0 additions */
622
    case HB_SCRIPT_CHORASMIAN:
623
    case HB_SCRIPT_YEZIDI:
624

625
    /* Unicode-14.0 additions */
626
    case HB_SCRIPT_OLD_UYGHUR:
627

628
      return HB_DIRECTION_RTL;
629

630

631
    /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
632
    case HB_SCRIPT_OLD_HUNGARIAN:
633
    case HB_SCRIPT_OLD_ITALIC:
634
    case HB_SCRIPT_RUNIC:
635
    case HB_SCRIPT_TIFINAGH:
636

637
      return HB_DIRECTION_INVALID;
638
  }
639

640
  return HB_DIRECTION_LTR;
641
}
642

643

644
/* hb_version */
645

646

647
/**
648
 * SECTION:hb-version
649
 * @title: hb-version
650
 * @short_description: Information about the version of HarfBuzz in use
651
 * @include: hb.h
652
 *
653
 * These functions and macros allow accessing version of the HarfBuzz
654
 * library used at compile- as well as run-time, and to direct code
655
 * conditionally based on those versions, again, at compile- or run-time.
656
 **/
657

658

659
/**
660
 * hb_version:
661
 * @major: (out): Library major version component
662
 * @minor: (out): Library minor version component
663
 * @micro: (out): Library micro version component
664
 *
665
 * Returns library version as three integer components.
666
 *
667
 * Since: 0.9.2
668
 **/
669
void
670
hb_version (unsigned int *major,
671
            unsigned int *minor,
672
            unsigned int *micro)
673
{
674
  *major = HB_VERSION_MAJOR;
675
  *minor = HB_VERSION_MINOR;
676
  *micro = HB_VERSION_MICRO;
677
}
678

679
/**
680
 * hb_version_string:
681
 *
682
 * Returns library version as a string with three components.
683
 *
684
 * Return value: Library version string
685
 *
686
 * Since: 0.9.2
687
 **/
688
const char *
689
hb_version_string ()
690
{
691
  return HB_VERSION_STRING;
692
}
693

694
/**
695
 * hb_version_atleast:
696
 * @major: Library major version component
697
 * @minor: Library minor version component
698
 * @micro: Library micro version component
699
 *
700
 * Tests the library version against a minimum value,
701
 * as three integer components.
702
 *
703
 * Return value: `true` if the library is equal to or greater than
704
 * the test value, `false` otherwise
705
 *
706
 * Since: 0.9.30
707
 **/
708
hb_bool_t
709
hb_version_atleast (unsigned int major,
710
                    unsigned int minor,
711
                    unsigned int micro)
712
{
713
  return HB_VERSION_ATLEAST (major, minor, micro);
714
}
715

716

717

718
/* hb_feature_t and hb_variation_t */
719

720
static bool
721
parse_space (const char **pp, const char *end)
722
{
723
  while (*pp < end && ISSPACE (**pp))
724
    (*pp)++;
725
  return true;
726
}
727

728
static bool
729
parse_char (const char **pp, const char *end, char c)
730
{
731
  parse_space (pp, end);
732

733
  if (*pp == end || **pp != c)
734
    return false;
735

736
  (*pp)++;
737
  return true;
738
}
739

740
static bool
741
parse_uint (const char **pp, const char *end, unsigned int *pv)
742
{
743
  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
744
   * such that -1 turns into "big number"... */
745
  int v;
746
  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
747

748
  *pv = v;
749
  return true;
750
}
751

752
static bool
753
parse_uint32 (const char **pp, const char *end, uint32_t *pv)
754
{
755
  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
756
   * such that -1 turns into "big number"... */
757
  int v;
758
  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
759

760
  *pv = v;
761
  return true;
762
}
763

764
static bool
765
parse_bool (const char **pp, const char *end, uint32_t *pv)
766
{
767
  parse_space (pp, end);
768

769
  const char *p = *pp;
770
  while (*pp < end && ISALPHA(**pp))
771
    (*pp)++;
772

773
  /* CSS allows on/off as aliases 1/0. */
774
  if (*pp - p == 2
775
      && TOLOWER (p[0]) == 'o'
776
      && TOLOWER (p[1]) == 'n')
777
    *pv = 1;
778
  else if (*pp - p == 3
779
           && TOLOWER (p[0]) == 'o'
780
           && TOLOWER (p[1]) == 'f'
781
           && TOLOWER (p[2]) == 'f')
782
    *pv = 0;
783
  else
784
    return false;
785

786
  return true;
787
}
788

789
/* hb_feature_t */
790

791
static bool
792
parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
793
{
794
  if (parse_char (pp, end, '-'))
795
    feature->value = 0;
796
  else {
797
    parse_char (pp, end, '+');
798
    feature->value = 1;
799
  }
800

801
  return true;
802
}
803

804
static bool
805
parse_tag (const char **pp, const char *end, hb_tag_t *tag)
806
{
807
  parse_space (pp, end);
808

809
  char quote = 0;
810

811
  if (*pp < end && (**pp == '\'' || **pp == '"'))
812
  {
813
    quote = **pp;
814
    (*pp)++;
815
  }
816

817
  const char *p = *pp;
818
  while (*pp < end && (**pp != ' ' && **pp != '=' && **pp != '[' && **pp != quote))
819
    (*pp)++;
820

821
  if (p == *pp || *pp - p > 4)
822
    return false;
823

824
  *tag = hb_tag_from_string (p, *pp - p);
825

826
  if (quote)
827
  {
828
    /* CSS expects exactly four bytes.  And we only allow quotations for
829
     * CSS compatibility.  So, enforce the length. */
830
     if (*pp - p != 4)
831
       return false;
832
    if (*pp == end || **pp != quote)
833
      return false;
834
    (*pp)++;
835
  }
836

837
  return true;
838
}
839

840
static bool
841
parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
842
{
843
  parse_space (pp, end);
844

845
  bool has_start;
846

847
  feature->start = HB_FEATURE_GLOBAL_START;
848
  feature->end = HB_FEATURE_GLOBAL_END;
849

850
  if (!parse_char (pp, end, '['))
851
    return true;
852

853
  has_start = parse_uint (pp, end, &feature->start);
854

855
  if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
856
    parse_uint (pp, end, &feature->end);
857
  } else {
858
    if (has_start)
859
      feature->end = feature->start + 1;
860
  }
861

862
  return parse_char (pp, end, ']');
863
}
864

865
static bool
866
parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
867
{
868
  bool had_equal = parse_char (pp, end, '=');
869
  bool had_value = parse_uint32 (pp, end, &feature->value) ||
870
                   parse_bool (pp, end, &feature->value);
871
  /* CSS doesn't use equal-sign between tag and value.
872
   * If there was an equal-sign, then there *must* be a value.
873
   * A value without an equal-sign is ok, but not required. */
874
  return !had_equal || had_value;
875
}
876

877
static bool
878
parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
879
{
880
  return parse_feature_value_prefix (pp, end, feature) &&
881
         parse_tag (pp, end, &feature->tag) &&
882
         parse_feature_indices (pp, end, feature) &&
883
         parse_feature_value_postfix (pp, end, feature) &&
884
         parse_space (pp, end) &&
885
         *pp == end;
886
}
887

888
/**
889
 * hb_feature_from_string:
890
 * @str: (array length=len) (element-type uint8_t): a string to parse
891
 * @len: length of @str, or -1 if string is `NULL` terminated
892
 * @feature: (out): the #hb_feature_t to initialize with the parsed values
893
 *
894
 * Parses a string into a #hb_feature_t.
895
 *
896
 * The format for specifying feature strings follows. All valid CSS
897
 * font-feature-settings values other than 'normal' and the global values are
898
 * also accepted, though not documented below. CSS string escapes are not
899
 * supported.
900
 *
901
 * The range indices refer to the positions between Unicode characters. The
902
 * position before the first character is always 0.
903
 *
904
 * The format is Python-esque.  Here is how it all works:
905
 *
906
 * <informaltable pgwide='1' align='left' frame='none'>
907
 * <tgroup cols='5'>
908
 * <thead>
909
 * <row><entry>Syntax</entry>    <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
910
 * </thead>
911
 * <tbody>
912
 * <row><entry>Setting value:</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</entry>     <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
916
 * <row><entry>kern=0</entry>    <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
917
 * <row><entry>kern=1</entry>    <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
918
 * <row><entry>aalt=2</entry>    <entry>2</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Choose 2nd alternate</entry></row>
919
 * <row><entry>Setting index:</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>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
922
 * <row><entry>kern[5:]</entry>  <entry>1</entry>     <entry>5</entry>      <entry>∞</entry>   <entry>Turn feature on, partial</entry></row>
923
 * <row><entry>kern[:5]</entry>  <entry>1</entry>     <entry>0</entry>      <entry>5</entry>   <entry>Turn feature on, partial</entry></row>
924
 * <row><entry>kern[3:5]</entry> <entry>1</entry>     <entry>3</entry>      <entry>5</entry>   <entry>Turn feature on, range</entry></row>
925
 * <row><entry>kern[3]</entry>   <entry>1</entry>     <entry>3</entry>      <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
926
 * <row><entry>Mixing it all:</entry></row>
927
 * <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>
928
 * </tbody>
929
 * </tgroup>
930
 * </informaltable>
931
 *
932
 * Return value:
933
 * `true` if @str is successfully parsed, `false` otherwise
934
 *
935
 * Since: 0.9.5
936
 **/
937
hb_bool_t
938
hb_feature_from_string (const char *str, int len,
939
                        hb_feature_t *feature)
940
{
941
  hb_feature_t feat;
942

943
  if (len < 0)
944
    len = strlen (str);
945

946
  if (likely (parse_one_feature (&str, str + len, &feat)))
947
  {
948
    if (feature)
949
      *feature = feat;
950
    return true;
951
  }
952

953
  if (feature)
954
    hb_memset (feature, 0, sizeof (*feature));
955
  return false;
956
}
957

958
/**
959
 * hb_feature_to_string:
960
 * @feature: an #hb_feature_t to convert
961
 * @buf: (array length=size) (out): output string
962
 * @size: the allocated size of @buf
963
 *
964
 * Converts a #hb_feature_t into a `NULL`-terminated string in the format
965
 * understood by hb_feature_from_string(). The client in responsible for
966
 * allocating big enough size for @buf, 128 bytes is more than enough.
967
 *
968
 * Since: 0.9.5
969
 **/
970
void
971
hb_feature_to_string (hb_feature_t *feature,
972
                      char *buf, unsigned int size)
973
{
974
  if (unlikely (!size)) return;
975

976
  char s[128];
977
  unsigned int len = 0;
978
  if (feature->value == 0)
979
    s[len++] = '-';
980
  hb_tag_to_string (feature->tag, s + len);
981
  len += 4;
982
  while (len && s[len - 1] == ' ')
983
    len--;
984
  if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
985
  {
986
    s[len++] = '[';
987
    if (feature->start)
988
      len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
989
    if (feature->end != feature->start + 1) {
990
      s[len++] = ':';
991
      if (feature->end != HB_FEATURE_GLOBAL_END)
992
        len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
993
    }
994
    s[len++] = ']';
995
  }
996
  if (feature->value > 1)
997
  {
998
    s[len++] = '=';
999
    len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
1000
  }
1001
  assert (len < ARRAY_LENGTH (s));
1002
  len = hb_min (len, size - 1);
1003
  hb_memcpy (buf, s, len);
1004
  buf[len] = '\0';
1005
}
1006

1007
/* hb_variation_t */
1008

1009
static bool
1010
parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
1011
{
1012
  parse_char (pp, end, '='); /* Optional. */
1013
  double v;
1014
  if (unlikely (!hb_parse_double (pp, end, &v))) return false;
1015

1016
  variation->value = v;
1017
  return true;
1018
}
1019

1020
static bool
1021
parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
1022
{
1023
  return parse_tag (pp, end, &variation->tag) &&
1024
         parse_variation_value (pp, end, variation) &&
1025
         parse_space (pp, end) &&
1026
         *pp == end;
1027
}
1028

1029
/**
1030
 * hb_variation_from_string:
1031
 * @str: (array length=len) (element-type uint8_t): a string to parse
1032
 * @len: length of @str, or -1 if string is `NULL` terminated
1033
 * @variation: (out): the #hb_variation_t to initialize with the parsed values
1034
 *
1035
 * Parses a string into a #hb_variation_t.
1036
 *
1037
 * The format for specifying variation settings follows. All valid CSS
1038
 * font-variation-settings values other than 'normal' and 'inherited' are also
1039
 * accepted, though, not documented below.
1040
 *
1041
 * The format is a tag, optionally followed by an equals sign, followed by a
1042
 * number. For example `wght=500`, or `slnt=-7.5`.
1043
 *
1044
 * Return value:
1045
 * `true` if @str is successfully parsed, `false` otherwise
1046
 *
1047
 * Since: 1.4.2
1048
 */
1049
hb_bool_t
1050
hb_variation_from_string (const char *str, int len,
1051
                          hb_variation_t *variation)
1052
{
1053
  hb_variation_t var;
1054

1055
  if (len < 0)
1056
    len = strlen (str);
1057

1058
  if (likely (parse_one_variation (&str, str + len, &var)))
1059
  {
1060
    if (variation)
1061
      *variation = var;
1062
    return true;
1063
  }
1064

1065
  if (variation)
1066
    hb_memset (variation, 0, sizeof (*variation));
1067
  return false;
1068
}
1069

1070
#ifndef HB_NO_SETLOCALE
1071

1072
static inline void free_static_C_locale ();
1073

1074
static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,
1075
                                                           hb_C_locale_lazy_loader_t>
1076
{
1077
  static hb_locale_t create ()
1078
  {
1079
    hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
1080
    if (!l)
1081
      return l;
1082

1083
    hb_atexit (free_static_C_locale);
1084

1085
    return l;
1086
  }
1087
  static void destroy (hb_locale_t l)
1088
  {
1089
    freelocale (l);
1090
  }
1091
  static hb_locale_t get_null ()
1092
  {
1093
    return (hb_locale_t) 0;
1094
  }
1095
} static_C_locale;
1096

1097
static inline
1098
void free_static_C_locale ()
1099
{
1100
  static_C_locale.free_instance ();
1101
}
1102

1103
static hb_locale_t
1104
get_C_locale ()
1105
{
1106
  return static_C_locale.get_unconst ();
1107
}
1108

1109
#endif
1110

1111
/**
1112
 * hb_variation_to_string:
1113
 * @variation: an #hb_variation_t to convert
1114
 * @buf: (array length=size) (out caller-allocates): output string
1115
 * @size: the allocated size of @buf
1116
 *
1117
 * Converts an #hb_variation_t into a `NULL`-terminated string in the format
1118
 * understood by hb_variation_from_string(). The client in responsible for
1119
 * allocating big enough size for @buf, 128 bytes is more than enough.
1120
 *
1121
 * Since: 1.4.2
1122
 */
1123
void
1124
hb_variation_to_string (hb_variation_t *variation,
1125
                        char *buf, unsigned int size)
1126
{
1127
  if (unlikely (!size)) return;
1128

1129
  char s[128];
1130
  unsigned int len = 0;
1131
  hb_tag_to_string (variation->tag, s + len);
1132
  len += 4;
1133
  while (len && s[len - 1] == ' ')
1134
    len--;
1135
  s[len++] = '=';
1136

1137
  hb_locale_t oldlocale HB_UNUSED;
1138
  oldlocale = hb_uselocale (get_C_locale ());
1139
  len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
1140
  (void) hb_uselocale (oldlocale);
1141

1142
  assert (len < ARRAY_LENGTH (s));
1143
  len = hb_min (len, size - 1);
1144
  hb_memcpy (buf, s, len);
1145
  buf[len] = '\0';
1146
}
1147

1148
/**
1149
 * hb_color_get_alpha:
1150
 * @color: an #hb_color_t we are interested in its channels.
1151
 *
1152
 * Fetches the alpha channel of the given @color.
1153
 *
1154
 * Return value: Alpha channel value
1155
 *
1156
 * Since: 2.1.0
1157
 */
1158
uint8_t
1159
(hb_color_get_alpha) (hb_color_t color)
1160
{
1161
  return hb_color_get_alpha (color);
1162
}
1163

1164
/**
1165
 * hb_color_get_red:
1166
 * @color: an #hb_color_t we are interested in its channels.
1167
 *
1168
 * Fetches the red channel of the given @color.
1169
 *
1170
 * Return value: Red channel value
1171
 *
1172
 * Since: 2.1.0
1173
 */
1174
uint8_t
1175
(hb_color_get_red) (hb_color_t color)
1176
{
1177
  return hb_color_get_red (color);
1178
}
1179

1180
/**
1181
 * hb_color_get_green:
1182
 * @color: an #hb_color_t we are interested in its channels.
1183
 *
1184
 * Fetches the green channel of the given @color.
1185
 *
1186
 * Return value: Green channel value
1187
 *
1188
 * Since: 2.1.0
1189
 */
1190
uint8_t
1191
(hb_color_get_green) (hb_color_t color)
1192
{
1193
  return hb_color_get_green (color);
1194
}
1195

1196
/**
1197
 * hb_color_get_blue:
1198
 * @color: an #hb_color_t we are interested in its channels.
1199
 *
1200
 * Fetches the blue channel of the given @color.
1201
 *
1202
 * Return value: Blue channel value
1203
 *
1204
 * Since: 2.1.0
1205
 */
1206
uint8_t
1207
(hb_color_get_blue) (hb_color_t color)
1208
{
1209
  return hb_color_get_blue (color);
1210
}
1211

1212

1213
/* If there is no visibility control, then hb-static.cc will NOT
1214
 * define anything.  Instead, we get it to define one set in here
1215
 * only, so only libharfbuzz.so defines them, not other libs. */
1216
#ifdef HB_NO_VISIBILITY
1217
#undef HB_NO_VISIBILITY
1218
#include "hb-static.cc"
1219
#define HB_NO_VISIBILITY 1
1220
#endif
1221

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.