1 /* 2 * Copyright 2009 Red Hat, Inc. 3 * Copyright 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-private.hh" 30 31 #include "hb-shaper-private.hh" 32 #include "hb-shape-plan-private.hh" 33 #include "hb-buffer-private.hh" 34 #include "hb-font-private.hh" 35 36 37 static void 38 parse_space (const char **pp, const char *end) 39 { 40 char c; 41 while (*pp < end && (c = **pp, ISSPACE (c))) 42 (*pp)++; 43 } 44 45 static hb_bool_t 46 parse_char (const char **pp, const char *end, char c) 47 { 48 parse_space (pp, end); 49 50 if (*pp == end || **pp != c) 51 return false; 52 53 (*pp)++; 54 return true; 55 } 56 57 static hb_bool_t 58 parse_uint (const char **pp, const char *end, unsigned int *pv) 59 { 60 char buf[32]; 61 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); 62 strncpy (buf, *pp, len); 63 buf[len] = '\0'; 64 65 char *p = buf; 66 char *pend = p; 67 unsigned int v; 68 69 /* Intentionally use strtol instead of strtoul, such that 70 * -1 turns into "big number"... */ 71 errno = 0; 72 v = strtol (p, &pend, 0); 73 if (errno || p == pend) 74 return false; 75 76 *pv = v; 77 *pp += pend - p; 78 return true; 79 } 80 81 static hb_bool_t 82 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) 83 { 84 if (parse_char (pp, end, '-')) 85 feature->value = 0; 86 else { 87 parse_char (pp, end, '+'); 88 feature->value = 1; 89 } 90 91 return true; 92 } 93 94 static hb_bool_t 95 parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature) 96 { 97 const char *p = *pp; 98 char c; 99 100 parse_space (pp, end); 101 102 #define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9')) 103 while (*pp < end && (c = **pp, ISALNUM(c))) 104 (*pp)++; 105 #undef ISALNUM 106 107 if (p == *pp) 108 return false; 109 110 feature->tag = hb_tag_from_string (p, *pp - p); 111 return true; 112 } 113 114 static hb_bool_t 115 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) 116 { 117 parse_space (pp, end); 118 119 hb_bool_t has_start; 120 121 feature->start = 0; 122 feature->end = (unsigned int) -1; 123 124 if (!parse_char (pp, end, '[')) 125 return true; 126 127 has_start = parse_uint (pp, end, &feature->start); 128 129 if (parse_char (pp, end, ':')) { 130 parse_uint (pp, end, &feature->end); 131 } else { 132 if (has_start) 133 feature->end = feature->start + 1; 134 } 135 136 return parse_char (pp, end, ']'); 137 } 138 139 static hb_bool_t 140 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) 141 { 142 return !parse_char (pp, end, '=') || parse_uint (pp, end, &feature->value); 143 } 144 145 146 static hb_bool_t 147 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) 148 { 149 return parse_feature_value_prefix (pp, end, feature) && 150 parse_feature_tag (pp, end, feature) && 151 parse_feature_indices (pp, end, feature) && 152 parse_feature_value_postfix (pp, end, feature) && 153 *pp == end; 154 } 155 156 hb_bool_t 157 hb_feature_from_string (const char *str, int len, 158 hb_feature_t *feature) 159 { 160 if (len < 0) 161 len = strlen (str); 162 163 return parse_one_feature (&str, str + len, feature); 164 } 165 166 void 167 hb_feature_to_string (hb_feature_t *feature, 168 char *buf, unsigned int size) 169 { 170 if (unlikely (!size)) return; 171 172 char s[128]; 173 unsigned int len = 0; 174 if (feature->value == 0) 175 s[len++] = '-'; 176 hb_tag_to_string (feature->tag, s + len); 177 len += 4; 178 while (len && s[len - 1] == ' ') 179 len--; 180 if (feature->start != 0 || feature->end != (unsigned int) -1) 181 { 182 s[len++] = '['; 183 if (feature->start) 184 len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->start); 185 if (feature->end != feature->start + 1) { 186 s[len++] = ':'; 187 if (feature->end != (unsigned int) -1) 188 len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->end); 189 } 190 s[len++] = ']'; 191 } 192 if (feature->value > 1) 193 { 194 s[len++] = '='; 195 len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->value); 196 } 197 assert (len < ARRAY_LENGTH (s)); 198 len = MIN (len, size - 1); 199 memcpy (buf, s, len); 200 s[len] = '\0'; 201 } 202 203 204 static const char **static_shaper_list; 205 206 static inline 207 void free_static_shaper_list (void) 208 { 209 free (static_shaper_list); 210 } 211 212 const char ** 213 hb_shape_list_shapers (void) 214 { 215 retry: 216 const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list); 217 218 if (unlikely (!shaper_list)) 219 { 220 /* Not found; allocate one. */ 221 shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *)); 222 if (unlikely (!shaper_list)) { 223 static const char *nil_shaper_list[] = {NULL}; 224 return nil_shaper_list; 225 } 226 227 const hb_shaper_pair_t *shapers = _hb_shapers_get (); 228 unsigned int i; 229 for (i = 0; i < HB_SHAPERS_COUNT; i++) 230 shaper_list[i] = shapers[i].name; 231 shaper_list[i] = NULL; 232 233 if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) { 234 free (shaper_list); 235 goto retry; 236 } 237 238 #ifdef HAVE_ATEXIT 239 atexit (free_static_shaper_list); /* First person registers atexit() callback. */ 240 #endif 241 } 242 243 return shaper_list; 244 } 245 246 247 hb_bool_t 248 hb_shape_full (hb_font_t *font, 249 hb_buffer_t *buffer, 250 const hb_feature_t *features, 251 unsigned int num_features, 252 const char * const *shaper_list) 253 { 254 if (unlikely (!buffer->len)) 255 return true; 256 257 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE); 258 259 hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list); 260 hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); 261 hb_shape_plan_destroy (shape_plan); 262 263 if (res) 264 buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; 265 return res; 266 } 267 268 void 269 hb_shape (hb_font_t *font, 270 hb_buffer_t *buffer, 271 const hb_feature_t *features, 272 unsigned int num_features) 273 { 274 hb_shape_full (font, buffer, features, num_features, NULL); 275 } 276