1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 // This file is available under and governed by the GNU General Public 26 // License version 2 only, as published by the Free Software Foundation. 27 // However, the following notice accompanied the original version of this 28 // file: 29 // 30 /* 31 * Copyright © 2009 Red Hat, Inc. 32 * Copyright © 2012 Google, Inc. 33 * 34 * This is part of HarfBuzz, a text shaping library. 35 * 36 * Permission is hereby granted, without written agreement and without 37 * license or royalty fees, to use, copy, modify, and distribute this 38 * software and its documentation for any purpose, provided that the 39 * above copyright notice and the following two paragraphs appear in 40 * all copies of this software. 41 * 42 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 43 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 44 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 45 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 46 * DAMAGE. 47 * 48 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 49 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 50 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 51 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 52 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 53 * 54 * Red Hat Author(s): Behdad Esfahbod 55 * Google Author(s): Behdad Esfahbod 56 */ 57 58 #include "hb-private.hh" 59 60 #include "hb-shaper-private.hh" 61 #include "hb-shape-plan-private.hh" 62 #include "hb-buffer-private.hh" 63 #include "hb-font-private.hh" 64 65 /** 66 * SECTION:hb-shape 67 * @title: Shaping 68 * @short_description: Conversion of text strings into positioned glyphs 69 * @include: hb.h 70 * 71 * Shaping is the central operation of HarfBuzz. Shaping operates on buffers, 72 * which are sequences of Unicode characters that use the same font and have 73 * the same text direction, script and language. After shaping the buffer 74 * contains the output glyphs and their positions. 75 **/ 76 77 static bool 78 parse_space (const char **pp, const char *end) 79 { 80 while (*pp < end && ISSPACE (**pp)) 81 (*pp)++; 82 return true; 83 } 84 85 static bool 86 parse_char (const char **pp, const char *end, char c) 87 { 88 parse_space (pp, end); 89 90 if (*pp == end || **pp != c) 91 return false; 92 93 (*pp)++; 94 return true; 95 } 96 97 static bool 98 parse_uint (const char **pp, const char *end, unsigned int *pv) 99 { 100 char buf[32]; 101 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); 102 strncpy (buf, *pp, len); 103 buf[len] = '\0'; 104 105 char *p = buf; 106 char *pend = p; 107 unsigned int v; 108 109 /* Intentionally use strtol instead of strtoul, such that 110 * -1 turns into "big number"... */ 111 errno = 0; 112 v = strtol (p, &pend, 0); 113 if (errno || p == pend) 114 return false; 115 116 *pv = v; 117 *pp += pend - p; 118 return true; 119 } 120 121 static bool 122 parse_bool (const char **pp, const char *end, unsigned int *pv) 123 { 124 parse_space (pp, end); 125 126 const char *p = *pp; 127 while (*pp < end && ISALPHA(**pp)) 128 (*pp)++; 129 130 /* CSS allows on/off as aliases 1/0. */ 131 if (*pp - p == 2 || 0 == strncmp (p, "on", 2)) 132 *pv = 1; 133 else if (*pp - p == 3 || 0 == strncmp (p, "off", 2)) 134 *pv = 0; 135 else 136 return false; 137 138 return true; 139 } 140 141 static bool 142 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) 143 { 144 if (parse_char (pp, end, '-')) 145 feature->value = 0; 146 else { 147 parse_char (pp, end, '+'); 148 feature->value = 1; 149 } 150 151 return true; 152 } 153 154 static bool 155 parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature) 156 { 157 parse_space (pp, end); 158 159 char quote = 0; 160 161 if (*pp < end && (**pp == '\'' || **pp == '"')) 162 { 163 quote = **pp; 164 (*pp)++; 165 } 166 167 const char *p = *pp; 168 while (*pp < end && ISALNUM(**pp)) 169 (*pp)++; 170 171 if (p == *pp || *pp - p > 4) 172 return false; 173 174 feature->tag = hb_tag_from_string (p, *pp - p); 175 176 if (quote) 177 { 178 /* CSS expects exactly four bytes. And we only allow quotations for 179 * CSS compatibility. So, enforce the length. */ 180 if (*pp - p != 4) 181 return false; 182 if (*pp == end || **pp != quote) 183 return false; 184 (*pp)++; 185 } 186 187 return true; 188 } 189 190 static bool 191 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) 192 { 193 parse_space (pp, end); 194 195 bool has_start; 196 197 feature->start = 0; 198 feature->end = (unsigned int) -1; 199 200 if (!parse_char (pp, end, '[')) 201 return true; 202 203 has_start = parse_uint (pp, end, &feature->start); 204 205 if (parse_char (pp, end, ':')) { 206 parse_uint (pp, end, &feature->end); 207 } else { 208 if (has_start) 209 feature->end = feature->start + 1; 210 } 211 212 return parse_char (pp, end, ']'); 213 } 214 215 static bool 216 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) 217 { 218 bool had_equal = parse_char (pp, end, '='); 219 bool had_value = parse_uint (pp, end, &feature->value) || 220 parse_bool (pp, end, &feature->value); 221 /* CSS doesn't use equal-sign between tag and value. 222 * If there was an equal-sign, then there *must* be a value. 223 * A value without an eqaul-sign is ok, but not required. */ 224 return !had_equal || had_value; 225 } 226 227 228 static bool 229 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) 230 { 231 return parse_feature_value_prefix (pp, end, feature) && 232 parse_feature_tag (pp, end, feature) && 233 parse_feature_indices (pp, end, feature) && 234 parse_feature_value_postfix (pp, end, feature) && 235 parse_space (pp, end) && 236 *pp == end; 237 } 238 239 /** 240 * hb_feature_from_string: 241 * @str: (array length=len) (element-type uint8_t): a string to parse 242 * @len: length of @str, or -1 if string is nul-terminated 243 * @feature: (out): the #hb_feature_t to initialize with the parsed values 244 * 245 * Parses a string into a #hb_feature_t. If @len is -1 then @str is 246 * %NULL-terminated. 247 * 248 * Return value: %TRUE if @str is successfully parsed, %FALSE otherwise 249 * 250 * Since: 0.9.5 251 **/ 252 hb_bool_t 253 hb_feature_from_string (const char *str, int len, 254 hb_feature_t *feature) 255 { 256 hb_feature_t feat; 257 258 if (len < 0) 259 len = strlen (str); 260 261 if (likely (parse_one_feature (&str, str + len, &feat))) 262 { 263 if (feature) 264 *feature = feat; 265 return true; 266 } 267 268 if (feature) 269 memset (feature, 0, sizeof (*feature)); 270 return false; 271 } 272 273 /** 274 * hb_feature_to_string: 275 * @feature: an #hb_feature_t to convert 276 * @buf: (array length=size) (out): output string 277 * @size: the allocated size of @buf 278 * 279 * Converts a #hb_feature_t into a %NULL-terminated string in the format 280 * understood by hb_feature_from_string(). The client in responsible for 281 * allocating big enough size for @buf, 128 bytes is more than enough. 282 * 283 * Since: 0.9.5 284 **/ 285 void 286 hb_feature_to_string (hb_feature_t *feature, 287 char *buf, unsigned int size) 288 { 289 if (unlikely (!size)) return; 290 291 char s[128]; 292 unsigned int len = 0; 293 if (feature->value == 0) 294 s[len++] = '-'; 295 hb_tag_to_string (feature->tag, s + len); 296 len += 4; 297 while (len && s[len - 1] == ' ') 298 len--; 299 if (feature->start != 0 || feature->end != (unsigned int) -1) 300 { 301 s[len++] = '['; 302 if (feature->start) 303 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); 304 if (feature->end != feature->start + 1) { 305 s[len++] = ':'; 306 if (feature->end != (unsigned int) -1) 307 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); 308 } 309 s[len++] = ']'; 310 } 311 if (feature->value > 1) 312 { 313 s[len++] = '='; 314 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); 315 } 316 assert (len < ARRAY_LENGTH (s)); 317 len = MIN (len, size - 1); 318 memcpy (buf, s, len); 319 buf[len] = '\0'; 320 } 321 322 323 static const char **static_shaper_list; 324 325 #ifdef HB_USE_ATEXIT 326 static 327 void free_static_shaper_list (void) 328 { 329 free (static_shaper_list); 330 } 331 #endif 332 333 /** 334 * hb_shape_list_shapers: 335 * 336 * Retrieves the list of shapers supported by HarfBuzz. 337 * 338 * Return value: (transfer none) (array zero-terminated=1): an array of 339 * constant strings 340 * 341 * Since: 0.9.2 342 **/ 343 const char ** 344 hb_shape_list_shapers (void) 345 { 346 retry: 347 const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list); 348 349 if (unlikely (!shaper_list)) 350 { 351 /* Not found; allocate one. */ 352 shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *)); 353 if (unlikely (!shaper_list)) { 354 static const char *nil_shaper_list[] = {NULL}; 355 return nil_shaper_list; 356 } 357 358 const hb_shaper_pair_t *shapers = _hb_shapers_get (); 359 unsigned int i; 360 for (i = 0; i < HB_SHAPERS_COUNT; i++) 361 shaper_list[i] = shapers[i].name; 362 shaper_list[i] = NULL; 363 364 if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) { 365 free (shaper_list); 366 goto retry; 367 } 368 369 #ifdef HB_USE_ATEXIT 370 atexit (free_static_shaper_list); /* First person registers atexit() callback. */ 371 #endif 372 } 373 374 return shaper_list; 375 } 376 377 378 /** 379 * hb_shape_full: 380 * @font: an #hb_font_t to use for shaping 381 * @buffer: an #hb_buffer_t to shape 382 * @features: (array length=num_features) (allow-none): an array of user 383 * specified #hb_feature_t or %NULL 384 * @num_features: the length of @features array 385 * @shaper_list: (array zero-terminated=1) (allow-none): a %NULL-terminated 386 * array of shapers to use or %NULL 387 * 388 * See hb_shape() for details. If @shaper_list is not %NULL, the specified 389 * shapers will be used in the given order, otherwise the default shapers list 390 * will be used. 391 * 392 * Return value: %FALSE if all shapers failed, %TRUE otherwise 393 * 394 * Since: 0.9.2 395 **/ 396 hb_bool_t 397 hb_shape_full (hb_font_t *font, 398 hb_buffer_t *buffer, 399 const hb_feature_t *features, 400 unsigned int num_features, 401 const char * const *shaper_list) 402 { 403 hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list); 404 hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); 405 hb_shape_plan_destroy (shape_plan); 406 407 if (res) 408 buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; 409 return res; 410 } 411 412 /** 413 * hb_shape: 414 * @font: an #hb_font_t to use for shaping 415 * @buffer: an #hb_buffer_t to shape 416 * @features: (array length=num_features) (allow-none): an array of user 417 * specified #hb_feature_t or %NULL 418 * @num_features: the length of @features array 419 * 420 * Shapes @buffer using @font turning its Unicode characters content to 421 * positioned glyphs. If @features is not %NULL, it will be used to control the 422 * features applied during shaping. 423 * 424 * Return value: %FALSE if all shapers failed, %TRUE otherwise 425 * 426 * Since: 0.9.2 427 **/ 428 void 429 hb_shape (hb_font_t *font, 430 hb_buffer_t *buffer, 431 const hb_feature_t *features, 432 unsigned int num_features) 433 { 434 hb_shape_full (font, buffer, features, num_features, NULL); 435 }