Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/harfbuzz/src/hb-common.cc @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 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 = 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 hb_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 hb_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_language_matches: | |
| 446 * @language: The #hb_language_t to work on | |
| 447 * @specific: Another #hb_language_t | |
| 448 * | |
| 449 * Check whether a second language tag is the same or a more | |
| 450 * specific version of the provided language tag. For example, | |
| 451 * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR". | |
| 452 * | |
| 453 * Return value: `true` if languages match, `false` otherwise. | |
| 454 * | |
| 455 * Since: 5.0.0 | |
| 456 **/ | |
| 457 hb_bool_t | |
| 458 hb_language_matches (hb_language_t language, | |
| 459 hb_language_t specific) | |
| 460 { | |
| 461 if (language == specific) return true; | |
| 462 if (!language || !specific) return false; | |
| 463 | |
| 464 const char *l = language->s; | |
| 465 const char *s = specific->s; | |
| 466 unsigned ll = strlen (l); | |
| 467 unsigned sl = strlen (s); | |
| 468 | |
| 469 if (ll > sl) | |
| 470 return false; | |
| 471 | |
| 472 return strncmp (l, s, ll) == 0 && | |
| 473 (s[ll] == '\0' || s[ll] == '-'); | |
| 474 } | |
| 475 | |
| 476 | |
| 477 /* hb_script_t */ | |
| 478 | |
| 479 /** | |
| 480 * hb_script_from_iso15924_tag: | |
| 481 * @tag: an #hb_tag_t representing an ISO 15924 tag. | |
| 482 * | |
| 483 * Converts an ISO 15924 script tag to a corresponding #hb_script_t. | |
| 484 * | |
| 485 * Return value: | |
| 486 * An #hb_script_t corresponding to the ISO 15924 tag. | |
| 487 * | |
| 488 * Since: 0.9.2 | |
| 489 **/ | |
| 490 hb_script_t | |
| 491 hb_script_from_iso15924_tag (hb_tag_t tag) | |
| 492 { | |
| 493 if (unlikely (tag == HB_TAG_NONE)) | |
| 494 return HB_SCRIPT_INVALID; | |
| 495 | |
| 496 /* Be lenient, adjust case (one capital letter followed by three small letters) */ | |
| 497 tag = (tag & 0xDFDFDFDFu) | 0x00202020u; | |
| 498 | |
| 499 switch (tag) { | |
| 500 | |
| 501 /* These graduated from the 'Q' private-area codes, but | |
| 502 * the old code is still aliased by Unicode, and the Qaai | |
| 503 * one in use by ICU. */ | |
| 504 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; | |
| 505 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; | |
| 506 | |
| 507 /* Script variants from https://unicode.org/iso15924/ */ | |
| 508 case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC; | |
| 509 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; | |
| 510 case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN; | |
| 511 case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN; | |
| 512 case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN; | |
| 513 case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL; | |
| 514 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; | |
| 515 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; | |
| 516 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; | |
| 517 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC; | |
| 518 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC; | |
| 519 } | |
| 520 | |
| 521 /* If it looks right, just use the tag as a script */ | |
| 522 if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u) | |
| 523 return (hb_script_t) tag; | |
| 524 | |
| 525 /* Otherwise, return unknown */ | |
| 526 return HB_SCRIPT_UNKNOWN; | |
| 527 } | |
| 528 | |
| 529 /** | |
| 530 * hb_script_from_string: | |
| 531 * @str: (array length=len) (element-type uint8_t): a string representing an | |
| 532 * ISO 15924 tag. | |
| 533 * @len: length of the @str, or -1 if it is `NULL`-terminated. | |
| 534 * | |
| 535 * Converts a string @str representing an ISO 15924 script tag to a | |
| 536 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then | |
| 537 * hb_script_from_iso15924_tag(). | |
| 538 * | |
| 539 * Return value: | |
| 540 * An #hb_script_t corresponding to the ISO 15924 tag. | |
| 541 * | |
| 542 * Since: 0.9.2 | |
| 543 **/ | |
| 544 hb_script_t | |
| 545 hb_script_from_string (const char *str, int len) | |
| 546 { | |
| 547 return hb_script_from_iso15924_tag (hb_tag_from_string (str, len)); | |
| 548 } | |
| 549 | |
| 550 /** | |
| 551 * hb_script_to_iso15924_tag: | |
| 552 * @script: an #hb_script_t to convert. | |
| 553 * | |
| 554 * Converts an #hb_script_t to a corresponding ISO 15924 script tag. | |
| 555 * | |
| 556 * Return value: | |
| 557 * An #hb_tag_t representing an ISO 15924 script tag. | |
| 558 * | |
| 559 * Since: 0.9.2 | |
| 560 **/ | |
| 561 hb_tag_t | |
| 562 hb_script_to_iso15924_tag (hb_script_t script) | |
| 563 { | |
| 564 return (hb_tag_t) script; | |
| 565 } | |
| 566 | |
| 567 /** | |
| 568 * hb_script_get_horizontal_direction: | |
| 569 * @script: The #hb_script_t to query | |
| 570 * | |
| 571 * Fetches the #hb_direction_t of a script when it is | |
| 572 * set horizontally. All right-to-left scripts will return | |
| 573 * #HB_DIRECTION_RTL. All left-to-right scripts will return | |
| 574 * #HB_DIRECTION_LTR. Scripts that can be written either | |
| 575 * horizontally or vertically will return #HB_DIRECTION_INVALID. | |
| 576 * Unknown scripts will return #HB_DIRECTION_LTR. | |
| 577 * | |
| 578 * Return value: The horizontal #hb_direction_t of @script | |
| 579 * | |
| 580 * Since: 0.9.2 | |
| 581 **/ | |
| 582 hb_direction_t | |
| 583 hb_script_get_horizontal_direction (hb_script_t script) | |
| 584 { | |
| 585 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ | |
| 586 switch ((hb_tag_t) script) | |
| 587 { | |
| 588 /* Unicode-1.1 additions */ | |
| 589 case HB_SCRIPT_ARABIC: | |
| 590 case HB_SCRIPT_HEBREW: | |
| 591 | |
| 592 /* Unicode-3.0 additions */ | |
| 593 case HB_SCRIPT_SYRIAC: | |
| 594 case HB_SCRIPT_THAANA: | |
| 595 | |
| 596 /* Unicode-4.0 additions */ | |
| 597 case HB_SCRIPT_CYPRIOT: | |
| 598 | |
| 599 /* Unicode-4.1 additions */ | |
| 600 case HB_SCRIPT_KHAROSHTHI: | |
| 601 | |
| 602 /* Unicode-5.0 additions */ | |
| 603 case HB_SCRIPT_PHOENICIAN: | |
| 604 case HB_SCRIPT_NKO: | |
| 605 | |
| 606 /* Unicode-5.1 additions */ | |
| 607 case HB_SCRIPT_LYDIAN: | |
| 608 | |
| 609 /* Unicode-5.2 additions */ | |
| 610 case HB_SCRIPT_AVESTAN: | |
| 611 case HB_SCRIPT_IMPERIAL_ARAMAIC: | |
| 612 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: | |
| 613 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: | |
| 614 case HB_SCRIPT_OLD_SOUTH_ARABIAN: | |
| 615 case HB_SCRIPT_OLD_TURKIC: | |
| 616 case HB_SCRIPT_SAMARITAN: | |
| 617 | |
| 618 /* Unicode-6.0 additions */ | |
| 619 case HB_SCRIPT_MANDAIC: | |
| 620 | |
| 621 /* Unicode-6.1 additions */ | |
| 622 case HB_SCRIPT_MEROITIC_CURSIVE: | |
| 623 case HB_SCRIPT_MEROITIC_HIEROGLYPHS: | |
| 624 | |
| 625 /* Unicode-7.0 additions */ | |
| 626 case HB_SCRIPT_MANICHAEAN: | |
| 627 case HB_SCRIPT_MENDE_KIKAKUI: | |
| 628 case HB_SCRIPT_NABATAEAN: | |
| 629 case HB_SCRIPT_OLD_NORTH_ARABIAN: | |
| 630 case HB_SCRIPT_PALMYRENE: | |
| 631 case HB_SCRIPT_PSALTER_PAHLAVI: | |
| 632 | |
| 633 /* Unicode-8.0 additions */ | |
| 634 case HB_SCRIPT_HATRAN: | |
| 635 | |
| 636 /* Unicode-9.0 additions */ | |
| 637 case HB_SCRIPT_ADLAM: | |
| 638 | |
| 639 /* Unicode-11.0 additions */ | |
| 640 case HB_SCRIPT_HANIFI_ROHINGYA: | |
| 641 case HB_SCRIPT_OLD_SOGDIAN: | |
| 642 case HB_SCRIPT_SOGDIAN: | |
| 643 | |
| 644 /* Unicode-12.0 additions */ | |
| 645 case HB_SCRIPT_ELYMAIC: | |
| 646 | |
| 647 /* Unicode-13.0 additions */ | |
| 648 case HB_SCRIPT_CHORASMIAN: | |
| 649 case HB_SCRIPT_YEZIDI: | |
| 650 | |
| 651 /* Unicode-14.0 additions */ | |
| 652 case HB_SCRIPT_OLD_UYGHUR: | |
| 653 | |
| 654 return HB_DIRECTION_RTL; | |
| 655 | |
| 656 | |
| 657 /* https://github.com/harfbuzz/harfbuzz/issues/1000 */ | |
| 658 case HB_SCRIPT_OLD_HUNGARIAN: | |
| 659 case HB_SCRIPT_OLD_ITALIC: | |
| 660 case HB_SCRIPT_RUNIC: | |
| 661 | |
| 662 return HB_DIRECTION_INVALID; | |
| 663 } | |
| 664 | |
| 665 return HB_DIRECTION_LTR; | |
| 666 } | |
| 667 | |
| 668 | |
| 669 /* hb_version */ | |
| 670 | |
| 671 | |
| 672 /** | |
| 673 * SECTION:hb-version | |
| 674 * @title: hb-version | |
| 675 * @short_description: Information about the version of HarfBuzz in use | |
| 676 * @include: hb.h | |
| 677 * | |
| 678 * These functions and macros allow accessing version of the HarfBuzz | |
| 679 * library used at compile- as well as run-time, and to direct code | |
| 680 * conditionally based on those versions, again, at compile- or run-time. | |
| 681 **/ | |
| 682 | |
| 683 | |
| 684 /** | |
| 685 * hb_version: | |
| 686 * @major: (out): Library major version component | |
| 687 * @minor: (out): Library minor version component | |
| 688 * @micro: (out): Library micro version component | |
| 689 * | |
| 690 * Returns library version as three integer components. | |
| 691 * | |
| 692 * Since: 0.9.2 | |
| 693 **/ | |
| 694 void | |
| 695 hb_version (unsigned int *major, | |
| 696 unsigned int *minor, | |
| 697 unsigned int *micro) | |
| 698 { | |
| 699 *major = HB_VERSION_MAJOR; | |
| 700 *minor = HB_VERSION_MINOR; | |
| 701 *micro = HB_VERSION_MICRO; | |
| 702 } | |
| 703 | |
| 704 /** | |
| 705 * hb_version_string: | |
| 706 * | |
| 707 * Returns library version as a string with three components. | |
| 708 * | |
| 709 * Return value: Library version string | |
| 710 * | |
| 711 * Since: 0.9.2 | |
| 712 **/ | |
| 713 const char * | |
| 714 hb_version_string () | |
| 715 { | |
| 716 return HB_VERSION_STRING; | |
| 717 } | |
| 718 | |
| 719 /** | |
| 720 * hb_version_atleast: | |
| 721 * @major: Library major version component | |
| 722 * @minor: Library minor version component | |
| 723 * @micro: Library micro version component | |
| 724 * | |
| 725 * Tests the library version against a minimum value, | |
| 726 * as three integer components. | |
| 727 * | |
| 728 * Return value: `true` if the library is equal to or greater than | |
| 729 * the test value, `false` otherwise | |
| 730 * | |
| 731 * Since: 0.9.30 | |
| 732 **/ | |
| 733 hb_bool_t | |
| 734 hb_version_atleast (unsigned int major, | |
| 735 unsigned int minor, | |
| 736 unsigned int micro) | |
| 737 { | |
| 738 return HB_VERSION_ATLEAST (major, minor, micro); | |
| 739 } | |
| 740 | |
| 741 | |
| 742 | |
| 743 /* hb_feature_t and hb_variation_t */ | |
| 744 | |
| 745 static bool | |
| 746 parse_space (const char **pp, const char *end) | |
| 747 { | |
| 748 while (*pp < end && ISSPACE (**pp)) | |
| 749 (*pp)++; | |
| 750 return true; | |
| 751 } | |
| 752 | |
| 753 static bool | |
| 754 parse_char (const char **pp, const char *end, char c) | |
| 755 { | |
| 756 parse_space (pp, end); | |
| 757 | |
| 758 if (*pp == end || **pp != c) | |
| 759 return false; | |
| 760 | |
| 761 (*pp)++; | |
| 762 return true; | |
| 763 } | |
| 764 | |
| 765 static bool | |
| 766 parse_uint (const char **pp, const char *end, unsigned int *pv) | |
| 767 { | |
| 768 /* Intentionally use hb_parse_int inside instead of hb_parse_uint, | |
| 769 * such that -1 turns into "big number"... */ | |
| 770 int v; | |
| 771 if (unlikely (!hb_parse_int (pp, end, &v))) return false; | |
| 772 | |
| 773 *pv = v; | |
| 774 return true; | |
| 775 } | |
| 776 | |
| 777 static bool | |
| 778 parse_uint32 (const char **pp, const char *end, uint32_t *pv) | |
| 779 { | |
| 780 /* Intentionally use hb_parse_int inside instead of hb_parse_uint, | |
| 781 * such that -1 turns into "big number"... */ | |
| 782 int v; | |
| 783 if (unlikely (!hb_parse_int (pp, end, &v))) return false; | |
| 784 | |
| 785 *pv = v; | |
| 786 return true; | |
| 787 } | |
| 788 | |
| 789 static bool | |
| 790 parse_bool (const char **pp, const char *end, uint32_t *pv) | |
| 791 { | |
| 792 parse_space (pp, end); | |
| 793 | |
| 794 const char *p = *pp; | |
| 795 while (*pp < end && ISALPHA(**pp)) | |
| 796 (*pp)++; | |
| 797 | |
| 798 /* CSS allows on/off as aliases 1/0. */ | |
| 799 if (*pp - p == 2 | |
| 800 && TOLOWER (p[0]) == 'o' | |
| 801 && TOLOWER (p[1]) == 'n') | |
| 802 *pv = 1; | |
| 803 else if (*pp - p == 3 | |
| 804 && TOLOWER (p[0]) == 'o' | |
| 805 && TOLOWER (p[1]) == 'f' | |
| 806 && TOLOWER (p[2]) == 'f') | |
| 807 *pv = 0; | |
| 808 else | |
| 809 return false; | |
| 810 | |
| 811 return true; | |
| 812 } | |
| 813 | |
| 814 /* hb_feature_t */ | |
| 815 | |
| 816 static bool | |
| 817 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) | |
| 818 { | |
| 819 if (parse_char (pp, end, '-')) | |
| 820 feature->value = 0; | |
| 821 else { | |
| 822 parse_char (pp, end, '+'); | |
| 823 feature->value = 1; | |
| 824 } | |
| 825 | |
| 826 return true; | |
| 827 } | |
| 828 | |
| 829 static bool | |
| 830 parse_tag (const char **pp, const char *end, hb_tag_t *tag) | |
| 831 { | |
| 832 parse_space (pp, end); | |
| 833 | |
| 834 char quote = 0; | |
| 835 | |
| 836 if (*pp < end && (**pp == '\'' || **pp == '"')) | |
| 837 { | |
| 838 quote = **pp; | |
| 839 (*pp)++; | |
| 840 } | |
| 841 | |
| 842 const char *p = *pp; | |
| 843 while (*pp < end && (ISALNUM(**pp) || **pp == '_')) | |
| 844 (*pp)++; | |
| 845 | |
| 846 if (p == *pp || *pp - p > 4) | |
| 847 return false; | |
| 848 | |
| 849 *tag = hb_tag_from_string (p, *pp - p); | |
| 850 | |
| 851 if (quote) | |
| 852 { | |
| 853 /* CSS expects exactly four bytes. And we only allow quotations for | |
| 854 * CSS compatibility. So, enforce the length. */ | |
| 855 if (*pp - p != 4) | |
| 856 return false; | |
| 857 if (*pp == end || **pp != quote) | |
| 858 return false; | |
| 859 (*pp)++; | |
| 860 } | |
| 861 | |
| 862 return true; | |
| 863 } | |
| 864 | |
| 865 static bool | |
| 866 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) | |
| 867 { | |
| 868 parse_space (pp, end); | |
| 869 | |
| 870 bool has_start; | |
| 871 | |
| 872 feature->start = HB_FEATURE_GLOBAL_START; | |
| 873 feature->end = HB_FEATURE_GLOBAL_END; | |
| 874 | |
| 875 if (!parse_char (pp, end, '[')) | |
| 876 return true; | |
| 877 | |
| 878 has_start = parse_uint (pp, end, &feature->start); | |
| 879 | |
| 880 if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) { | |
| 881 parse_uint (pp, end, &feature->end); | |
| 882 } else { | |
| 883 if (has_start) | |
| 884 feature->end = feature->start + 1; | |
| 885 } | |
| 886 | |
| 887 return parse_char (pp, end, ']'); | |
| 888 } | |
| 889 | |
| 890 static bool | |
| 891 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) | |
| 892 { | |
| 893 bool had_equal = parse_char (pp, end, '='); | |
| 894 bool had_value = parse_uint32 (pp, end, &feature->value) || | |
| 895 parse_bool (pp, end, &feature->value); | |
| 896 /* CSS doesn't use equal-sign between tag and value. | |
| 897 * If there was an equal-sign, then there *must* be a value. | |
| 898 * A value without an equal-sign is ok, but not required. */ | |
| 899 return !had_equal || had_value; | |
| 900 } | |
| 901 | |
| 902 static bool | |
| 903 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) | |
| 904 { | |
| 905 return parse_feature_value_prefix (pp, end, feature) && | |
| 906 parse_tag (pp, end, &feature->tag) && | |
| 907 parse_feature_indices (pp, end, feature) && | |
| 908 parse_feature_value_postfix (pp, end, feature) && | |
| 909 parse_space (pp, end) && | |
| 910 *pp == end; | |
| 911 } | |
| 912 | |
| 913 /** | |
| 914 * hb_feature_from_string: | |
| 915 * @str: (array length=len) (element-type uint8_t): a string to parse | |
| 916 * @len: length of @str, or -1 if string is `NULL` terminated | |
| 917 * @feature: (out): the #hb_feature_t to initialize with the parsed values | |
| 918 * | |
| 919 * Parses a string into a #hb_feature_t. | |
| 920 * | |
| 921 * The format for specifying feature strings follows. All valid CSS | |
| 922 * font-feature-settings values other than 'normal' and the global values are | |
| 923 * also accepted, though not documented below. CSS string escapes are not | |
| 924 * supported. | |
| 925 * | |
| 926 * The range indices refer to the positions between Unicode characters. The | |
| 927 * position before the first character is always 0. | |
| 928 * | |
| 929 * The format is Python-esque. Here is how it all works: | |
| 930 * | |
| 931 * <informaltable pgwide='1' align='left' frame='none'> | |
| 932 * <tgroup cols='5'> | |
| 933 * <thead> | |
| 934 * <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row> | |
| 935 * </thead> | |
| 936 * <tbody> | |
| 937 * <row><entry>Setting value:</entry></row> | |
| 938 * <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row> | |
| 939 * <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row> | |
| 940 * <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row> | |
| 941 * <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row> | |
| 942 * <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row> | |
| 943 * <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row> | |
| 944 * <row><entry>Setting index:</entry></row> | |
| 945 * <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row> | |
| 946 * <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row> | |
| 947 * <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row> | |
| 948 * <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row> | |
| 949 * <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row> | |
| 950 * <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row> | |
| 951 * <row><entry>Mixing it all:</entry></row> | |
| 952 * <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> | |
| 953 * </tbody> | |
| 954 * </tgroup> | |
| 955 * </informaltable> | |
| 956 * | |
| 957 * Return value: | |
| 958 * `true` if @str is successfully parsed, `false` otherwise | |
| 959 * | |
| 960 * Since: 0.9.5 | |
| 961 **/ | |
| 962 hb_bool_t | |
| 963 hb_feature_from_string (const char *str, int len, | |
| 964 hb_feature_t *feature) | |
| 965 { | |
| 966 hb_feature_t feat; | |
| 967 | |
| 968 if (len < 0) | |
| 969 len = strlen (str); | |
| 970 | |
| 971 if (likely (parse_one_feature (&str, str + len, &feat))) | |
| 972 { | |
| 973 if (feature) | |
| 974 *feature = feat; | |
| 975 return true; | |
| 976 } | |
| 977 | |
| 978 if (feature) | |
| 979 hb_memset (feature, 0, sizeof (*feature)); | |
| 980 return false; | |
| 981 } | |
| 982 | |
| 983 /** | |
| 984 * hb_feature_to_string: | |
| 985 * @feature: an #hb_feature_t to convert | |
| 986 * @buf: (array length=size) (out): output string | |
| 987 * @size: the allocated size of @buf | |
| 988 * | |
| 989 * Converts a #hb_feature_t into a `NULL`-terminated string in the format | |
| 990 * understood by hb_feature_from_string(). The client in responsible for | |
| 991 * allocating big enough size for @buf, 128 bytes is more than enough. | |
| 992 * | |
| 993 * Since: 0.9.5 | |
| 994 **/ | |
| 995 void | |
| 996 hb_feature_to_string (hb_feature_t *feature, | |
| 997 char *buf, unsigned int size) | |
| 998 { | |
| 999 if (unlikely (!size)) return; | |
| 1000 | |
| 1001 char s[128]; | |
| 1002 unsigned int len = 0; | |
| 1003 if (feature->value == 0) | |
| 1004 s[len++] = '-'; | |
| 1005 hb_tag_to_string (feature->tag, s + len); | |
| 1006 len += 4; | |
| 1007 while (len && s[len - 1] == ' ') | |
| 1008 len--; | |
| 1009 if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END) | |
| 1010 { | |
| 1011 s[len++] = '['; | |
| 1012 if (feature->start) | |
| 1013 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); | |
| 1014 if (feature->end != feature->start + 1) { | |
| 1015 s[len++] = ':'; | |
| 1016 if (feature->end != HB_FEATURE_GLOBAL_END) | |
| 1017 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); | |
| 1018 } | |
| 1019 s[len++] = ']'; | |
| 1020 } | |
| 1021 if (feature->value > 1) | |
| 1022 { | |
| 1023 s[len++] = '='; | |
| 1024 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); | |
| 1025 } | |
| 1026 assert (len < ARRAY_LENGTH (s)); | |
| 1027 len = hb_min (len, size - 1); | |
| 1028 hb_memcpy (buf, s, len); | |
| 1029 buf[len] = '\0'; | |
| 1030 } | |
| 1031 | |
| 1032 /* hb_variation_t */ | |
| 1033 | |
| 1034 static bool | |
| 1035 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation) | |
| 1036 { | |
| 1037 parse_char (pp, end, '='); /* Optional. */ | |
| 1038 double v; | |
| 1039 if (unlikely (!hb_parse_double (pp, end, &v))) return false; | |
| 1040 | |
| 1041 variation->value = v; | |
| 1042 return true; | |
| 1043 } | |
| 1044 | |
| 1045 static bool | |
| 1046 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation) | |
| 1047 { | |
| 1048 return parse_tag (pp, end, &variation->tag) && | |
| 1049 parse_variation_value (pp, end, variation) && | |
| 1050 parse_space (pp, end) && | |
| 1051 *pp == end; | |
| 1052 } | |
| 1053 | |
| 1054 /** | |
| 1055 * hb_variation_from_string: | |
| 1056 * @str: (array length=len) (element-type uint8_t): a string to parse | |
| 1057 * @len: length of @str, or -1 if string is `NULL` terminated | |
| 1058 * @variation: (out): the #hb_variation_t to initialize with the parsed values | |
| 1059 * | |
| 1060 * Parses a string into a #hb_variation_t. | |
| 1061 * | |
| 1062 * The format for specifying variation settings follows. All valid CSS | |
| 1063 * font-variation-settings values other than 'normal' and 'inherited' are also | |
| 1064 * accepted, though, not documented below. | |
| 1065 * | |
| 1066 * The format is a tag, optionally followed by an equals sign, followed by a | |
| 1067 * number. For example `wght=500`, or `slnt=-7.5`. | |
| 1068 * | |
| 1069 * Return value: | |
| 1070 * `true` if @str is successfully parsed, `false` otherwise | |
| 1071 * | |
| 1072 * Since: 1.4.2 | |
| 1073 */ | |
| 1074 hb_bool_t | |
| 1075 hb_variation_from_string (const char *str, int len, | |
| 1076 hb_variation_t *variation) | |
| 1077 { | |
| 1078 hb_variation_t var; | |
| 1079 | |
| 1080 if (len < 0) | |
| 1081 len = strlen (str); | |
| 1082 | |
| 1083 if (likely (parse_one_variation (&str, str + len, &var))) | |
| 1084 { | |
| 1085 if (variation) | |
| 1086 *variation = var; | |
| 1087 return true; | |
| 1088 } | |
| 1089 | |
| 1090 if (variation) | |
| 1091 hb_memset (variation, 0, sizeof (*variation)); | |
| 1092 return false; | |
| 1093 } | |
| 1094 | |
| 1095 #ifndef HB_NO_SETLOCALE | |
| 1096 | |
| 1097 static inline void free_static_C_locale (); | |
| 1098 | |
| 1099 static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>, | |
| 1100 hb_C_locale_lazy_loader_t> | |
| 1101 { | |
| 1102 static hb_locale_t create () | |
| 1103 { | |
| 1104 hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL); | |
| 1105 if (!l) | |
| 1106 return l; | |
| 1107 | |
| 1108 hb_atexit (free_static_C_locale); | |
| 1109 | |
| 1110 return l; | |
| 1111 } | |
| 1112 static void destroy (hb_locale_t l) | |
| 1113 { | |
| 1114 freelocale (l); | |
| 1115 } | |
| 1116 static hb_locale_t get_null () | |
| 1117 { | |
| 1118 return (hb_locale_t) 0; | |
| 1119 } | |
| 1120 } static_C_locale; | |
| 1121 | |
| 1122 static inline | |
| 1123 void free_static_C_locale () | |
| 1124 { | |
| 1125 static_C_locale.free_instance (); | |
| 1126 } | |
| 1127 | |
| 1128 static hb_locale_t | |
| 1129 get_C_locale () | |
| 1130 { | |
| 1131 return static_C_locale.get_unconst (); | |
| 1132 } | |
| 1133 | |
| 1134 #endif | |
| 1135 | |
| 1136 /** | |
| 1137 * hb_variation_to_string: | |
| 1138 * @variation: an #hb_variation_t to convert | |
| 1139 * @buf: (array length=size) (out caller-allocates): output string | |
| 1140 * @size: the allocated size of @buf | |
| 1141 * | |
| 1142 * Converts an #hb_variation_t into a `NULL`-terminated string in the format | |
| 1143 * understood by hb_variation_from_string(). The client in responsible for | |
| 1144 * allocating big enough size for @buf, 128 bytes is more than enough. | |
| 1145 * | |
| 1146 * Since: 1.4.2 | |
| 1147 */ | |
| 1148 void | |
| 1149 hb_variation_to_string (hb_variation_t *variation, | |
| 1150 char *buf, unsigned int size) | |
| 1151 { | |
| 1152 if (unlikely (!size)) return; | |
| 1153 | |
| 1154 char s[128]; | |
| 1155 unsigned int len = 0; | |
| 1156 hb_tag_to_string (variation->tag, s + len); | |
| 1157 len += 4; | |
| 1158 while (len && s[len - 1] == ' ') | |
| 1159 len--; | |
| 1160 s[len++] = '='; | |
| 1161 | |
| 1162 hb_locale_t oldlocale HB_UNUSED; | |
| 1163 oldlocale = hb_uselocale (get_C_locale ()); | |
| 1164 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value)); | |
| 1165 (void) hb_uselocale (oldlocale); | |
| 1166 | |
| 1167 assert (len < ARRAY_LENGTH (s)); | |
| 1168 len = hb_min (len, size - 1); | |
| 1169 hb_memcpy (buf, s, len); | |
| 1170 buf[len] = '\0'; | |
| 1171 } | |
| 1172 | |
| 1173 /** | |
| 1174 * hb_color_get_alpha: | |
| 1175 * @color: an #hb_color_t we are interested in its channels. | |
| 1176 * | |
| 1177 * Fetches the alpha channel of the given @color. | |
| 1178 * | |
| 1179 * Return value: Alpha channel value | |
| 1180 * | |
| 1181 * Since: 2.1.0 | |
| 1182 */ | |
| 1183 uint8_t | |
| 1184 (hb_color_get_alpha) (hb_color_t color) | |
| 1185 { | |
| 1186 return hb_color_get_alpha (color); | |
| 1187 } | |
| 1188 | |
| 1189 /** | |
| 1190 * hb_color_get_red: | |
| 1191 * @color: an #hb_color_t we are interested in its channels. | |
| 1192 * | |
| 1193 * Fetches the red channel of the given @color. | |
| 1194 * | |
| 1195 * Return value: Red channel value | |
| 1196 * | |
| 1197 * Since: 2.1.0 | |
| 1198 */ | |
| 1199 uint8_t | |
| 1200 (hb_color_get_red) (hb_color_t color) | |
| 1201 { | |
| 1202 return hb_color_get_red (color); | |
| 1203 } | |
| 1204 | |
| 1205 /** | |
| 1206 * hb_color_get_green: | |
| 1207 * @color: an #hb_color_t we are interested in its channels. | |
| 1208 * | |
| 1209 * Fetches the green channel of the given @color. | |
| 1210 * | |
| 1211 * Return value: Green channel value | |
| 1212 * | |
| 1213 * Since: 2.1.0 | |
| 1214 */ | |
| 1215 uint8_t | |
| 1216 (hb_color_get_green) (hb_color_t color) | |
| 1217 { | |
| 1218 return hb_color_get_green (color); | |
| 1219 } | |
| 1220 | |
| 1221 /** | |
| 1222 * hb_color_get_blue: | |
| 1223 * @color: an #hb_color_t we are interested in its channels. | |
| 1224 * | |
| 1225 * Fetches the blue channel of the given @color. | |
| 1226 * | |
| 1227 * Return value: Blue channel value | |
| 1228 * | |
| 1229 * Since: 2.1.0 | |
| 1230 */ | |
| 1231 uint8_t | |
| 1232 (hb_color_get_blue) (hb_color_t color) | |
| 1233 { | |
| 1234 return hb_color_get_blue (color); | |
| 1235 } | |
| 1236 | |
| 1237 | |
| 1238 /* If there is no visibility control, then hb-static.cc will NOT | |
| 1239 * define anything. Instead, we get it to define one set in here | |
| 1240 * only, so only libharfbuzz.so defines them, not other libs. */ | |
| 1241 #ifdef HB_NO_VISIBILITY | |
| 1242 #undef HB_NO_VISIBILITY | |
| 1243 #include "hb-static.cc" | |
| 1244 #define HB_NO_VISIBILITY 1 | |
| 1245 #endif |
