changeset 48274 | 51772bf1fb0c |
parent 47216 | 71c04702a3d5 |
child 50352 | 25db2c8f3cf8 |
48273:e2065f7505eb | 48274:51772bf1fb0c |
---|---|
30 |
30 |
31 #include "hb-mutex-private.hh" |
31 #include "hb-mutex-private.hh" |
32 #include "hb-object-private.hh" |
32 #include "hb-object-private.hh" |
33 |
33 |
34 #include <locale.h> |
34 #include <locale.h> |
35 #ifdef HAVE_XLOCALE_H |
|
36 #include <xlocale.h> |
|
37 #endif |
|
35 |
38 |
36 |
39 |
37 /* hb_options_t */ |
40 /* hb_options_t */ |
38 |
41 |
39 hb_options_union_t _hb_options; |
42 hb_options_union_t _hb_options; |
55 |
58 |
56 /* hb_tag_t */ |
59 /* hb_tag_t */ |
57 |
60 |
58 /** |
61 /** |
59 * hb_tag_from_string: |
62 * hb_tag_from_string: |
60 * @str: (array length=len) (element-type uint8_t): |
63 * @str: (array length=len) (element-type uint8_t): |
61 * @len: |
64 * @len: |
62 * |
65 * |
63 * |
66 * |
64 * |
67 * |
65 * Return value: |
68 * Return value: |
66 * |
69 * |
67 * Since: 0.9.2 |
70 * Since: 0.9.2 |
68 **/ |
71 **/ |
69 hb_tag_t |
72 hb_tag_t |
70 hb_tag_from_string (const char *str, int len) |
73 hb_tag_from_string (const char *str, int len) |
80 for (i = 0; i < (unsigned) len && str[i]; i++) |
83 for (i = 0; i < (unsigned) len && str[i]; i++) |
81 tag[i] = str[i]; |
84 tag[i] = str[i]; |
82 for (; i < 4; i++) |
85 for (; i < 4; i++) |
83 tag[i] = ' '; |
86 tag[i] = ' '; |
84 |
87 |
85 return HB_TAG_CHAR4 (tag); |
88 return HB_TAG (tag[0], tag[1], tag[2], tag[3]); |
86 } |
89 } |
87 |
90 |
88 /** |
91 /** |
89 * hb_tag_to_string: |
92 * hb_tag_to_string: |
90 * @tag: |
93 * @tag: |
91 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): |
94 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): |
92 * |
95 * |
93 * |
96 * |
94 * |
97 * |
95 * Since: 0.9.5 |
98 * Since: 0.9.5 |
96 **/ |
99 **/ |
97 void |
100 void |
98 hb_tag_to_string (hb_tag_t tag, char *buf) |
101 hb_tag_to_string (hb_tag_t tag, char *buf) |
113 "btt" |
116 "btt" |
114 }; |
117 }; |
115 |
118 |
116 /** |
119 /** |
117 * hb_direction_from_string: |
120 * hb_direction_from_string: |
118 * @str: (array length=len) (element-type uint8_t): |
121 * @str: (array length=len) (element-type uint8_t): |
119 * @len: |
122 * @len: |
120 * |
123 * |
121 * |
124 * |
122 * |
125 * |
123 * Return value: |
126 * Return value: |
124 * |
127 * |
125 * Since: 0.9.2 |
128 * Since: 0.9.2 |
126 **/ |
129 **/ |
127 hb_direction_t |
130 hb_direction_t |
128 hb_direction_from_string (const char *str, int len) |
131 hb_direction_from_string (const char *str, int len) |
141 return HB_DIRECTION_INVALID; |
144 return HB_DIRECTION_INVALID; |
142 } |
145 } |
143 |
146 |
144 /** |
147 /** |
145 * hb_direction_to_string: |
148 * hb_direction_to_string: |
146 * @direction: |
149 * @direction: |
147 * |
150 * |
148 * |
151 * |
149 * |
152 * |
150 * Return value: (transfer none): |
153 * Return value: (transfer none): |
151 * |
154 * |
152 * Since: 0.9.2 |
155 * Since: 0.9.2 |
153 **/ |
156 **/ |
154 const char * |
157 const char * |
155 hb_direction_to_string (hb_direction_t direction) |
158 hb_direction_to_string (hb_direction_t direction) |
184 const void *v2) |
187 const void *v2) |
185 { |
188 { |
186 const unsigned char *p1 = (const unsigned char *) v1; |
189 const unsigned char *p1 = (const unsigned char *) v1; |
187 const unsigned char *p2 = (const unsigned char *) v2; |
190 const unsigned char *p2 = (const unsigned char *) v2; |
188 |
191 |
189 while (*p1 && *p1 == canon_map[*p2]) |
192 while (*p1 && *p1 == canon_map[*p2]) { |
190 p1++, p2++; |
193 p1++; |
194 p2++; |
|
195 } |
|
191 |
196 |
192 return *p1 == canon_map[*p2]; |
197 return *p1 == canon_map[*p2]; |
193 } |
198 } |
194 |
199 |
195 #if 0 |
200 #if 0 |
217 inline bool operator == (const char *s) const { |
222 inline bool operator == (const char *s) const { |
218 return lang_equal (lang, s); |
223 return lang_equal (lang, s); |
219 } |
224 } |
220 |
225 |
221 inline hb_language_item_t & operator = (const char *s) { |
226 inline hb_language_item_t & operator = (const char *s) { |
222 lang = (hb_language_t) strdup (s); |
227 /* If a custom allocated is used calling strdup() pairs |
223 for (unsigned char *p = (unsigned char *) lang; *p; p++) |
228 badly with a call to the custom free() in finish() below. |
224 *p = canon_map[*p]; |
229 Therefore don't call strdup(), implement its behavior. |
230 */ |
|
231 size_t len = strlen(s) + 1; |
|
232 lang = (hb_language_t) malloc(len); |
|
233 if (likely (lang)) |
|
234 { |
|
235 memcpy((unsigned char *) lang, s, len); |
|
236 for (unsigned char *p = (unsigned char *) lang; *p; p++) |
|
237 *p = canon_map[*p]; |
|
238 } |
|
225 |
239 |
226 return *this; |
240 return *this; |
227 } |
241 } |
228 |
242 |
229 void finish (void) { free ((void *) lang); } |
243 void finish (void) { free ((void *) lang); } |
233 /* Thread-safe lock-free language list */ |
247 /* Thread-safe lock-free language list */ |
234 |
248 |
235 static hb_language_item_t *langs; |
249 static hb_language_item_t *langs; |
236 |
250 |
237 #ifdef HB_USE_ATEXIT |
251 #ifdef HB_USE_ATEXIT |
238 static |
252 static void |
239 void free_langs (void) |
253 free_langs (void) |
240 { |
254 { |
241 while (langs) { |
255 while (langs) { |
242 hb_language_item_t *next = langs->next; |
256 hb_language_item_t *next = langs->next; |
243 langs->finish (); |
257 langs->finish (); |
244 free (langs); |
258 free (langs); |
258 return lang; |
272 return lang; |
259 |
273 |
260 /* Not found; allocate one. */ |
274 /* Not found; allocate one. */ |
261 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); |
275 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); |
262 if (unlikely (!lang)) |
276 if (unlikely (!lang)) |
263 return NULL; |
277 return nullptr; |
264 lang->next = first_lang; |
278 lang->next = first_lang; |
265 *lang = key; |
279 *lang = key; |
280 if (unlikely (!lang->lang)) |
|
281 { |
|
282 free (lang); |
|
283 return nullptr; |
|
284 } |
|
266 |
285 |
267 if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) { |
286 if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) { |
268 lang->finish (); |
287 lang->finish (); |
269 free (lang); |
288 free (lang); |
270 goto retry; |
289 goto retry; |
297 hb_language_from_string (const char *str, int len) |
316 hb_language_from_string (const char *str, int len) |
298 { |
317 { |
299 if (!str || !len || !*str) |
318 if (!str || !len || !*str) |
300 return HB_LANGUAGE_INVALID; |
319 return HB_LANGUAGE_INVALID; |
301 |
320 |
302 hb_language_item_t *item = NULL; |
321 hb_language_item_t *item = nullptr; |
303 if (len >= 0) |
322 if (len >= 0) |
304 { |
323 { |
305 /* NUL-terminate it. */ |
324 /* NUL-terminate it. */ |
306 char strbuf[64]; |
325 char strbuf[64]; |
307 len = MIN (len, (int) sizeof (strbuf) - 1); |
326 len = MIN (len, (int) sizeof (strbuf) - 1); |
328 * Since: 0.9.2 |
347 * Since: 0.9.2 |
329 **/ |
348 **/ |
330 const char * |
349 const char * |
331 hb_language_to_string (hb_language_t language) |
350 hb_language_to_string (hb_language_t language) |
332 { |
351 { |
333 /* This is actually NULL-safe! */ |
352 /* This is actually nullptr-safe! */ |
334 return language->s; |
353 return language->s; |
335 } |
354 } |
336 |
355 |
337 /** |
356 /** |
338 * hb_language_get_default: |
357 * hb_language_get_default: |
339 * |
358 * |
340 * |
359 * |
341 * |
360 * |
342 * Return value: (transfer none): |
361 * Return value: (transfer none): |
343 * |
362 * |
344 * Since: 0.9.2 |
363 * Since: 0.9.2 |
345 **/ |
364 **/ |
348 { |
367 { |
349 static hb_language_t default_language = HB_LANGUAGE_INVALID; |
368 static hb_language_t default_language = HB_LANGUAGE_INVALID; |
350 |
369 |
351 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language); |
370 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language); |
352 if (unlikely (language == HB_LANGUAGE_INVALID)) { |
371 if (unlikely (language == HB_LANGUAGE_INVALID)) { |
353 language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1); |
372 language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1); |
354 (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language); |
373 (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language); |
355 } |
374 } |
356 |
375 |
357 return default_language; |
376 return default_language; |
358 } |
377 } |
364 * hb_script_from_iso15924_tag: |
383 * hb_script_from_iso15924_tag: |
365 * @tag: an #hb_tag_t representing an ISO 15924 tag. |
384 * @tag: an #hb_tag_t representing an ISO 15924 tag. |
366 * |
385 * |
367 * Converts an ISO 15924 script tag to a corresponding #hb_script_t. |
386 * Converts an ISO 15924 script tag to a corresponding #hb_script_t. |
368 * |
387 * |
369 * Return value: |
388 * Return value: |
370 * An #hb_script_t corresponding to the ISO 15924 tag. |
389 * An #hb_script_t corresponding to the ISO 15924 tag. |
371 * |
390 * |
372 * Since: 0.9.2 |
391 * Since: 0.9.2 |
373 **/ |
392 **/ |
374 hb_script_t |
393 hb_script_t |
413 * |
432 * |
414 * Converts a string @str representing an ISO 15924 script tag to a |
433 * Converts a string @str representing an ISO 15924 script tag to a |
415 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then |
434 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then |
416 * hb_script_from_iso15924_tag(). |
435 * hb_script_from_iso15924_tag(). |
417 * |
436 * |
418 * Return value: |
437 * Return value: |
419 * An #hb_script_t corresponding to the ISO 15924 tag. |
438 * An #hb_script_t corresponding to the ISO 15924 tag. |
420 * |
439 * |
421 * Since: 0.9.2 |
440 * Since: 0.9.2 |
422 **/ |
441 **/ |
423 hb_script_t |
442 hb_script_t |
443 return (hb_tag_t) script; |
462 return (hb_tag_t) script; |
444 } |
463 } |
445 |
464 |
446 /** |
465 /** |
447 * hb_script_get_horizontal_direction: |
466 * hb_script_get_horizontal_direction: |
448 * @script: |
467 * @script: |
449 * |
468 * |
450 * |
469 * |
451 * |
470 * |
452 * Return value: |
471 * Return value: |
453 * |
472 * |
454 * Since: 0.9.2 |
473 * Since: 0.9.2 |
455 **/ |
474 **/ |
456 hb_direction_t |
475 hb_direction_t |
457 hb_script_get_horizontal_direction (hb_script_t script) |
476 hb_script_get_horizontal_direction (hb_script_t script) |
541 } |
560 } |
542 |
561 |
543 void * |
562 void * |
544 hb_user_data_array_t::get (hb_user_data_key_t *key) |
563 hb_user_data_array_t::get (hb_user_data_key_t *key) |
545 { |
564 { |
546 hb_user_data_item_t item = {NULL, NULL, NULL}; |
565 hb_user_data_item_t item = {nullptr, nullptr, nullptr}; |
547 |
566 |
548 return items.find (key, &item, lock) ? item.data : NULL; |
567 return items.find (key, &item, lock) ? item.data : nullptr; |
549 } |
568 } |
550 |
569 |
551 |
570 |
552 /* hb_version */ |
571 /* hb_version */ |
553 |
572 |
586 return HB_VERSION_STRING; |
605 return HB_VERSION_STRING; |
587 } |
606 } |
588 |
607 |
589 /** |
608 /** |
590 * hb_version_atleast: |
609 * hb_version_atleast: |
591 * @major: |
610 * @major: |
592 * @minor: |
611 * @minor: |
593 * @micro: |
612 * @micro: |
594 * |
613 * |
595 * |
614 * |
596 * |
615 * |
597 * Return value: |
616 * Return value: |
598 * |
617 * |
599 * Since: 0.9.30 |
618 * Since: 0.9.30 |
600 **/ |
619 **/ |
601 hb_bool_t |
620 hb_bool_t |
602 hb_version_atleast (unsigned int major, |
621 hb_version_atleast (unsigned int major, |
603 unsigned int minor, |
622 unsigned int minor, |
604 unsigned int micro) |
623 unsigned int micro) |
605 { |
624 { |
606 return HB_VERSION_ATLEAST (major, minor, micro); |
625 return HB_VERSION_ATLEAST (major, minor, micro); |
607 } |
626 } |
627 |
|
628 |
|
629 |
|
630 /* hb_feature_t and hb_variation_t */ |
|
631 |
|
632 static bool |
|
633 parse_space (const char **pp, const char *end) |
|
634 { |
|
635 while (*pp < end && ISSPACE (**pp)) |
|
636 (*pp)++; |
|
637 return true; |
|
638 } |
|
639 |
|
640 static bool |
|
641 parse_char (const char **pp, const char *end, char c) |
|
642 { |
|
643 parse_space (pp, end); |
|
644 |
|
645 if (*pp == end || **pp != c) |
|
646 return false; |
|
647 |
|
648 (*pp)++; |
|
649 return true; |
|
650 } |
|
651 |
|
652 static bool |
|
653 parse_uint (const char **pp, const char *end, unsigned int *pv) |
|
654 { |
|
655 char buf[32]; |
|
656 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); |
|
657 strncpy (buf, *pp, len); |
|
658 buf[len] = '\0'; |
|
659 |
|
660 char *p = buf; |
|
661 char *pend = p; |
|
662 unsigned int v; |
|
663 |
|
664 /* Intentionally use strtol instead of strtoul, such that |
|
665 * -1 turns into "big number"... */ |
|
666 errno = 0; |
|
667 v = strtol (p, &pend, 0); |
|
668 if (errno || p == pend) |
|
669 return false; |
|
670 |
|
671 *pv = v; |
|
672 *pp += pend - p; |
|
673 return true; |
|
674 } |
|
675 |
|
676 static bool |
|
677 parse_uint32 (const char **pp, const char *end, uint32_t *pv) |
|
678 { |
|
679 char buf[32]; |
|
680 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); |
|
681 strncpy (buf, *pp, len); |
|
682 buf[len] = '\0'; |
|
683 |
|
684 char *p = buf; |
|
685 char *pend = p; |
|
686 unsigned int v; |
|
687 |
|
688 /* Intentionally use strtol instead of strtoul, such that |
|
689 * -1 turns into "big number"... */ |
|
690 errno = 0; |
|
691 v = strtol (p, &pend, 0); |
|
692 if (errno || p == pend) |
|
693 return false; |
|
694 |
|
695 *pv = v; |
|
696 *pp += pend - p; |
|
697 return true; |
|
698 } |
|
699 |
|
700 #if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L) |
|
701 #define USE_XLOCALE 1 |
|
702 #define HB_LOCALE_T locale_t |
|
703 #define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr) |
|
704 #define HB_FREE_LOCALE(loc) freelocale (loc) |
|
705 #elif defined(_MSC_VER) |
|
706 #define USE_XLOCALE 1 |
|
707 #define HB_LOCALE_T _locale_t |
|
708 #define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName) |
|
709 #define HB_FREE_LOCALE(loc) _free_locale (loc) |
|
710 #define strtod_l(a, b, c) _strtod_l ((a), (b), (c)) |
|
711 #endif |
|
712 |
|
713 #ifdef USE_XLOCALE |
|
714 |
|
715 static HB_LOCALE_T C_locale; |
|
716 |
|
717 #ifdef HB_USE_ATEXIT |
|
718 static void |
|
719 free_C_locale (void) |
|
720 { |
|
721 if (C_locale) |
|
722 HB_FREE_LOCALE (C_locale); |
|
723 } |
|
724 #endif |
|
725 |
|
726 static HB_LOCALE_T |
|
727 get_C_locale (void) |
|
728 { |
|
729 retry: |
|
730 HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale); |
|
731 |
|
732 if (unlikely (!C)) |
|
733 { |
|
734 C = HB_CREATE_LOCALE ("C"); |
|
735 |
|
736 if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C)) |
|
737 { |
|
738 HB_FREE_LOCALE (C_locale); |
|
739 goto retry; |
|
740 } |
|
741 |
|
742 #ifdef HB_USE_ATEXIT |
|
743 atexit (free_C_locale); /* First person registers atexit() callback. */ |
|
744 #endif |
|
745 } |
|
746 |
|
747 return C; |
|
748 } |
|
749 #endif |
|
750 |
|
751 static bool |
|
752 parse_float (const char **pp, const char *end, float *pv) |
|
753 { |
|
754 char buf[32]; |
|
755 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); |
|
756 strncpy (buf, *pp, len); |
|
757 buf[len] = '\0'; |
|
758 |
|
759 char *p = buf; |
|
760 char *pend = p; |
|
761 float v; |
|
762 |
|
763 errno = 0; |
|
764 #ifdef USE_XLOCALE |
|
765 v = strtod_l (p, &pend, get_C_locale ()); |
|
766 #else |
|
767 v = strtod (p, &pend); |
|
768 #endif |
|
769 if (errno || p == pend) |
|
770 return false; |
|
771 |
|
772 *pv = v; |
|
773 *pp += pend - p; |
|
774 return true; |
|
775 } |
|
776 |
|
777 static bool |
|
778 parse_bool (const char **pp, const char *end, uint32_t *pv) |
|
779 { |
|
780 parse_space (pp, end); |
|
781 |
|
782 const char *p = *pp; |
|
783 while (*pp < end && ISALPHA(**pp)) |
|
784 (*pp)++; |
|
785 |
|
786 /* CSS allows on/off as aliases 1/0. */ |
|
787 if (*pp - p == 2 || 0 == strncmp (p, "on", 2)) |
|
788 *pv = 1; |
|
789 else if (*pp - p == 3 || 0 == strncmp (p, "off", 2)) |
|
790 *pv = 0; |
|
791 else |
|
792 return false; |
|
793 |
|
794 return true; |
|
795 } |
|
796 |
|
797 /* hb_feature_t */ |
|
798 |
|
799 static bool |
|
800 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) |
|
801 { |
|
802 if (parse_char (pp, end, '-')) |
|
803 feature->value = 0; |
|
804 else { |
|
805 parse_char (pp, end, '+'); |
|
806 feature->value = 1; |
|
807 } |
|
808 |
|
809 return true; |
|
810 } |
|
811 |
|
812 static bool |
|
813 parse_tag (const char **pp, const char *end, hb_tag_t *tag) |
|
814 { |
|
815 parse_space (pp, end); |
|
816 |
|
817 char quote = 0; |
|
818 |
|
819 if (*pp < end && (**pp == '\'' || **pp == '"')) |
|
820 { |
|
821 quote = **pp; |
|
822 (*pp)++; |
|
823 } |
|
824 |
|
825 const char *p = *pp; |
|
826 while (*pp < end && ISALNUM(**pp)) |
|
827 (*pp)++; |
|
828 |
|
829 if (p == *pp || *pp - p > 4) |
|
830 return false; |
|
831 |
|
832 *tag = hb_tag_from_string (p, *pp - p); |
|
833 |
|
834 if (quote) |
|
835 { |
|
836 /* CSS expects exactly four bytes. And we only allow quotations for |
|
837 * CSS compatibility. So, enforce the length. */ |
|
838 if (*pp - p != 4) |
|
839 return false; |
|
840 if (*pp == end || **pp != quote) |
|
841 return false; |
|
842 (*pp)++; |
|
843 } |
|
844 |
|
845 return true; |
|
846 } |
|
847 |
|
848 static bool |
|
849 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) |
|
850 { |
|
851 parse_space (pp, end); |
|
852 |
|
853 bool has_start; |
|
854 |
|
855 feature->start = 0; |
|
856 feature->end = (unsigned int) -1; |
|
857 |
|
858 if (!parse_char (pp, end, '[')) |
|
859 return true; |
|
860 |
|
861 has_start = parse_uint (pp, end, &feature->start); |
|
862 |
|
863 if (parse_char (pp, end, ':')) { |
|
864 parse_uint (pp, end, &feature->end); |
|
865 } else { |
|
866 if (has_start) |
|
867 feature->end = feature->start + 1; |
|
868 } |
|
869 |
|
870 return parse_char (pp, end, ']'); |
|
871 } |
|
872 |
|
873 static bool |
|
874 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) |
|
875 { |
|
876 bool had_equal = parse_char (pp, end, '='); |
|
877 bool had_value = parse_uint32 (pp, end, &feature->value) || |
|
878 parse_bool (pp, end, &feature->value); |
|
879 /* CSS doesn't use equal-sign between tag and value. |
|
880 * If there was an equal-sign, then there *must* be a value. |
|
881 * A value without an eqaul-sign is ok, but not required. */ |
|
882 return !had_equal || had_value; |
|
883 } |
|
884 |
|
885 static bool |
|
886 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) |
|
887 { |
|
888 return parse_feature_value_prefix (pp, end, feature) && |
|
889 parse_tag (pp, end, &feature->tag) && |
|
890 parse_feature_indices (pp, end, feature) && |
|
891 parse_feature_value_postfix (pp, end, feature) && |
|
892 parse_space (pp, end) && |
|
893 *pp == end; |
|
894 } |
|
895 |
|
896 /** |
|
897 * hb_feature_from_string: |
|
898 * @str: (array length=len) (element-type uint8_t): a string to parse |
|
899 * @len: length of @str, or -1 if string is %NULL terminated |
|
900 * @feature: (out): the #hb_feature_t to initialize with the parsed values |
|
901 * |
|
902 * Parses a string into a #hb_feature_t. |
|
903 * |
|
904 * TODO: document the syntax here. |
|
905 * |
|
906 * Return value: |
|
907 * %true if @str is successfully parsed, %false otherwise. |
|
908 * |
|
909 * Since: 0.9.5 |
|
910 **/ |
|
911 hb_bool_t |
|
912 hb_feature_from_string (const char *str, int len, |
|
913 hb_feature_t *feature) |
|
914 { |
|
915 hb_feature_t feat; |
|
916 |
|
917 if (len < 0) |
|
918 len = strlen (str); |
|
919 |
|
920 if (likely (parse_one_feature (&str, str + len, &feat))) |
|
921 { |
|
922 if (feature) |
|
923 *feature = feat; |
|
924 return true; |
|
925 } |
|
926 |
|
927 if (feature) |
|
928 memset (feature, 0, sizeof (*feature)); |
|
929 return false; |
|
930 } |
|
931 |
|
932 /** |
|
933 * hb_feature_to_string: |
|
934 * @feature: an #hb_feature_t to convert |
|
935 * @buf: (array length=size) (out): output string |
|
936 * @size: the allocated size of @buf |
|
937 * |
|
938 * Converts a #hb_feature_t into a %NULL-terminated string in the format |
|
939 * understood by hb_feature_from_string(). The client in responsible for |
|
940 * allocating big enough size for @buf, 128 bytes is more than enough. |
|
941 * |
|
942 * Since: 0.9.5 |
|
943 **/ |
|
944 void |
|
945 hb_feature_to_string (hb_feature_t *feature, |
|
946 char *buf, unsigned int size) |
|
947 { |
|
948 if (unlikely (!size)) return; |
|
949 |
|
950 char s[128]; |
|
951 unsigned int len = 0; |
|
952 if (feature->value == 0) |
|
953 s[len++] = '-'; |
|
954 hb_tag_to_string (feature->tag, s + len); |
|
955 len += 4; |
|
956 while (len && s[len - 1] == ' ') |
|
957 len--; |
|
958 if (feature->start != 0 || feature->end != (unsigned int) -1) |
|
959 { |
|
960 s[len++] = '['; |
|
961 if (feature->start) |
|
962 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); |
|
963 if (feature->end != feature->start + 1) { |
|
964 s[len++] = ':'; |
|
965 if (feature->end != (unsigned int) -1) |
|
966 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); |
|
967 } |
|
968 s[len++] = ']'; |
|
969 } |
|
970 if (feature->value > 1) |
|
971 { |
|
972 s[len++] = '='; |
|
973 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); |
|
974 } |
|
975 assert (len < ARRAY_LENGTH (s)); |
|
976 len = MIN (len, size - 1); |
|
977 memcpy (buf, s, len); |
|
978 buf[len] = '\0'; |
|
979 } |
|
980 |
|
981 /* hb_variation_t */ |
|
982 |
|
983 static bool |
|
984 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation) |
|
985 { |
|
986 parse_char (pp, end, '='); /* Optional. */ |
|
987 return parse_float (pp, end, &variation->value); |
|
988 } |
|
989 |
|
990 static bool |
|
991 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation) |
|
992 { |
|
993 return parse_tag (pp, end, &variation->tag) && |
|
994 parse_variation_value (pp, end, variation) && |
|
995 parse_space (pp, end) && |
|
996 *pp == end; |
|
997 } |
|
998 |
|
999 /** |
|
1000 * hb_variation_from_string: |
|
1001 * |
|
1002 * Since: 1.4.2 |
|
1003 */ |
|
1004 hb_bool_t |
|
1005 hb_variation_from_string (const char *str, int len, |
|
1006 hb_variation_t *variation) |
|
1007 { |
|
1008 hb_variation_t var; |
|
1009 |
|
1010 if (len < 0) |
|
1011 len = strlen (str); |
|
1012 |
|
1013 if (likely (parse_one_variation (&str, str + len, &var))) |
|
1014 { |
|
1015 if (variation) |
|
1016 *variation = var; |
|
1017 return true; |
|
1018 } |
|
1019 |
|
1020 if (variation) |
|
1021 memset (variation, 0, sizeof (*variation)); |
|
1022 return false; |
|
1023 } |
|
1024 |
|
1025 /** |
|
1026 * hb_variation_to_string: |
|
1027 * |
|
1028 * Since: 1.4.2 |
|
1029 */ |
|
1030 void |
|
1031 hb_variation_to_string (hb_variation_t *variation, |
|
1032 char *buf, unsigned int size) |
|
1033 { |
|
1034 if (unlikely (!size)) return; |
|
1035 |
|
1036 char s[128]; |
|
1037 unsigned int len = 0; |
|
1038 hb_tag_to_string (variation->tag, s + len); |
|
1039 len += 4; |
|
1040 while (len && s[len - 1] == ' ') |
|
1041 len--; |
|
1042 s[len++] = '='; |
|
1043 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value)); |
|
1044 |
|
1045 assert (len < ARRAY_LENGTH (s)); |
|
1046 len = MIN (len, size - 1); |
|
1047 memcpy (buf, s, len); |
|
1048 buf[len] = '\0'; |
|
1049 } |