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 /** 157 * hb_feature_from_string: 158 * @str: (array length=len): 159 * @len: 160 * @feature: (out): 161 * 162 * 163 * 164 * Return value: 165 * 166 * Since: 1.0 167 **/ 168 hb_bool_t 169 hb_feature_from_string (const char *str, int len, 170 hb_feature_t *feature) 171 { 172 if (len < 0) 173 len = strlen (str); 174 175 return parse_one_feature (&str, str + len, feature); 176 } 177 178 /** 179 * hb_feature_to_string: 180 * @feature: 181 * @buf: (array length=size): 182 * @size: 183 * 184 * 185 * 186 * Since: 1.0 187 **/ 188 void 189 hb_feature_to_string (hb_feature_t *feature, 190 char *buf, unsigned int size) 191 { 192 if (unlikely (!size)) return; 193 194 char s[128]; 195 unsigned int len = 0; 196 if (feature->value == 0) 197 s[len++] = '-'; 198 hb_tag_to_string (feature->tag, s + len); 199 len += 4; 200 while (len && s[len - 1] == ' ') 201 len--; 202 if (feature->start != 0 || feature->end != (unsigned int) -1) 203 { 204 s[len++] = '['; 205 if (feature->start) 206 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->start)); 207 if (feature->end != feature->start + 1) { 208 s[len++] = ':'; 209 if (feature->end != (unsigned int) -1) 210 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->end)); 211 } 212 s[len++] = ']'; 213 } 214 if (feature->value > 1) 215 { 216 s[len++] = '='; 217 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->value)); 218 } 219 assert (len < ARRAY_LENGTH (s)); 220 len = MIN (len, size - 1); 221 memcpy (buf, s, len); 222 buf[len] = '\0'; 223 } 224 225 226 static const char **static_shaper_list; 227 228 static inline 229 void free_static_shaper_list (void) 230 { 231 free (static_shaper_list); 232 } 233 234 /** 235 * hb_shape_list_shapers: 236 * 237 * 238 * 239 * Return value: (transfer none): 240 * 241 * Since: 1.0 242 **/ 243 const char ** 244 hb_shape_list_shapers (void) 245 { 246 retry: 247 const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list); 248 249 if (unlikely (!shaper_list)) 250 { 251 /* Not found; allocate one. */ 252 shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *)); 253 if (unlikely (!shaper_list)) { 254 static const char *nil_shaper_list[] = {NULL}; 255 return nil_shaper_list; 256 } 257 258 const hb_shaper_pair_t *shapers = _hb_shapers_get (); 259 unsigned int i; 260 for (i = 0; i < HB_SHAPERS_COUNT; i++) 261 shaper_list[i] = shapers[i].name; 262 shaper_list[i] = NULL; 263 264 if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) { 265 free (shaper_list); 266 goto retry; 267 } 268 269 #ifdef HAVE_ATEXIT 270 atexit (free_static_shaper_list); /* First person registers atexit() callback. */ 271 #endif 272 } 273 274 return shaper_list; 275 } 276 277 278 /** 279 * hb_shape_full: 280 * @font: a font. 281 * @buffer: a buffer. 282 * @features: (array length=num_features): 283 * @num_features: 284 * @shaper_list: (array zero-terminated=1): 285 * 286 * 287 * 288 * Return value: 289 * 290 * Since: 1.0 291 **/ 292 hb_bool_t 293 hb_shape_full (hb_font_t *font, 294 hb_buffer_t *buffer, 295 const hb_feature_t *features, 296 unsigned int num_features, 297 const char * const *shaper_list) 298 { 299 if (unlikely (!buffer->len)) 300 return true; 301 302 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE); 303 304 hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list); 305 hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); 306 hb_shape_plan_destroy (shape_plan); 307 308 if (res) 309 buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; 310 return res; 311 } 312 313 /** 314 * hb_shape: 315 * @font: a font. 316 * @buffer: a buffer. 317 * @features: (array length=num_features): 318 * @num_features: 319 * 320 * 321 * 322 * Since: 1.0 323 **/ 324 void 325 hb_shape (hb_font_t *font, 326 hb_buffer_t *buffer, 327 const hb_feature_t *features, 328 unsigned int num_features) 329 { 330 hb_shape_full (font, buffer, features, num_features, NULL); 331 } 332