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  * SECTION:hb-shape
  38  * @title: Shaping
  39  * @short_description: Conversion of text strings into positioned glyphs
  40  * @include: hb.h
  41  *
  42  * Shaping is the central operation of HarfBuzz. Shaping operates on buffers,
  43  * which are sequences of Unicode characters that use the same font and have
  44  * the same text direction, script and language. After shaping the buffer
  45  * contains the output glyphs and their positions.
  46  **/
  47 
  48 static bool
  49 parse_space (const char **pp, const char *end)
  50 {
  51   while (*pp < end && ISSPACE (**pp))
  52     (*pp)++;
  53   return true;
  54 }
  55 
  56 static bool
  57 parse_char (const char **pp, const char *end, char c)
  58 {
  59   parse_space (pp, end);
  60 
  61   if (*pp == end || **pp != c)
  62     return false;
  63 
  64   (*pp)++;
  65   return true;
  66 }
  67 
  68 static bool
  69 parse_uint (const char **pp, const char *end, unsigned int *pv)
  70 {
  71   char buf[32];
  72   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
  73   strncpy (buf, *pp, len);
  74   buf[len] = '\0';
  75 
  76   char *p = buf;
  77   char *pend = p;
  78   unsigned int v;
  79 
  80   /* Intentionally use strtol instead of strtoul, such that
  81    * -1 turns into "big number"... */
  82   errno = 0;
  83   v = strtol (p, &pend, 0);
  84   if (errno || p == pend)
  85     return false;
  86 
  87   *pv = v;
  88   *pp += pend - p;
  89   return true;
  90 }
  91 
  92 static bool
  93 parse_bool (const char **pp, const char *end, unsigned int *pv)
  94 {
  95   parse_space (pp, end);
  96 
  97   const char *p = *pp;
  98   while (*pp < end && ISALPHA(**pp))
  99     (*pp)++;
 100 
 101   /* CSS allows on/off as aliases 1/0. */
 102   if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
 103     *pv = 1;
 104   else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
 105     *pv = 0;
 106   else
 107     return false;
 108 
 109   return true;
 110 }
 111 
 112 static bool
 113 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
 114 {
 115   if (parse_char (pp, end, '-'))
 116     feature->value = 0;
 117   else {
 118     parse_char (pp, end, '+');
 119     feature->value = 1;
 120   }
 121 
 122   return true;
 123 }
 124 
 125 static bool
 126 parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature)
 127 {
 128   parse_space (pp, end);
 129 
 130   char quote = 0;
 131 
 132   if (*pp < end && (**pp == '\'' || **pp == '"'))
 133   {
 134     quote = **pp;
 135     (*pp)++;
 136   }
 137 
 138   const char *p = *pp;
 139   while (*pp < end && ISALNUM(**pp))
 140     (*pp)++;
 141 
 142   if (p == *pp || *pp - p > 4)
 143     return false;
 144 
 145   feature->tag = hb_tag_from_string (p, *pp - p);
 146 
 147   if (quote)
 148   {
 149     /* CSS expects exactly four bytes.  And we only allow quotations for
 150      * CSS compatibility.  So, enforce the length. */
 151      if (*pp - p != 4)
 152        return false;
 153     if (*pp == end || **pp != quote)
 154       return false;
 155     (*pp)++;
 156   }
 157 
 158   return true;
 159 }
 160 
 161 static bool
 162 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
 163 {
 164   parse_space (pp, end);
 165 
 166   bool has_start;
 167 
 168   feature->start = 0;
 169   feature->end = (unsigned int) -1;
 170 
 171   if (!parse_char (pp, end, '['))
 172     return true;
 173 
 174   has_start = parse_uint (pp, end, &feature->start);
 175 
 176   if (parse_char (pp, end, ':')) {
 177     parse_uint (pp, end, &feature->end);
 178   } else {
 179     if (has_start)
 180       feature->end = feature->start + 1;
 181   }
 182 
 183   return parse_char (pp, end, ']');
 184 }
 185 
 186 static bool
 187 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
 188 {
 189   bool had_equal = parse_char (pp, end, '=');
 190   bool had_value = parse_uint (pp, end, &feature->value) ||
 191                    parse_bool (pp, end, &feature->value);
 192   /* CSS doesn't use equal-sign between tag and value.
 193    * If there was an equal-sign, then there *must* be a value.
 194    * A value without an eqaul-sign is ok, but not required. */
 195   return !had_equal || had_value;
 196 }
 197 
 198 
 199 static bool
 200 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
 201 {
 202   return parse_feature_value_prefix (pp, end, feature) &&
 203          parse_feature_tag (pp, end, feature) &&
 204          parse_feature_indices (pp, end, feature) &&
 205          parse_feature_value_postfix (pp, end, feature) &&
 206          parse_space (pp, end) &&
 207          *pp == end;
 208 }
 209 
 210 /**
 211  * hb_feature_from_string:
 212  * @str: (array length=len) (element-type uint8_t): a string to parse
 213  * @len: length of @str, or -1 if string is nul-terminated
 214  * @feature: (out): the #hb_feature_t to initialize with the parsed values
 215  *
 216  * Parses a string into a #hb_feature_t. If @len is -1 then @str is
 217  * %NULL-terminated.
 218  *
 219  * Return value: %TRUE if @str is successfully parsed, %FALSE otherwise
 220  *
 221  * Since: 0.9.5
 222  **/
 223 hb_bool_t
 224 hb_feature_from_string (const char *str, int len,
 225                         hb_feature_t *feature)
 226 {
 227   hb_feature_t feat;
 228 
 229   if (len < 0)
 230     len = strlen (str);
 231 
 232   if (likely (parse_one_feature (&str, str + len, &feat)))
 233   {
 234     if (feature)
 235       *feature = feat;
 236     return true;
 237   }
 238 
 239   if (feature)
 240     memset (feature, 0, sizeof (*feature));
 241   return false;
 242 }
 243 
 244 /**
 245  * hb_feature_to_string:
 246  * @feature: an #hb_feature_t to convert
 247  * @buf: (array length=size) (out): output string
 248  * @size: the allocated size of @buf
 249  *
 250  * Converts a #hb_feature_t into a %NULL-terminated string in the format
 251  * understood by hb_feature_from_string(). The client in responsible for
 252  * allocating big enough size for @buf, 128 bytes is more than enough.
 253  *
 254  * Since: 0.9.5
 255  **/
 256 void
 257 hb_feature_to_string (hb_feature_t *feature,
 258                       char *buf, unsigned int size)
 259 {
 260   if (unlikely (!size)) return;
 261 
 262   char s[128];
 263   unsigned int len = 0;
 264   if (feature->value == 0)
 265     s[len++] = '-';
 266   hb_tag_to_string (feature->tag, s + len);
 267   len += 4;
 268   while (len && s[len - 1] == ' ')
 269     len--;
 270   if (feature->start != 0 || feature->end != (unsigned int) -1)
 271   {
 272     s[len++] = '[';
 273     if (feature->start)
 274       len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
 275     if (feature->end != feature->start + 1) {
 276       s[len++] = ':';
 277       if (feature->end != (unsigned int) -1)
 278         len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
 279     }
 280     s[len++] = ']';
 281   }
 282   if (feature->value > 1)
 283   {
 284     s[len++] = '=';
 285     len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
 286   }
 287   assert (len < ARRAY_LENGTH (s));
 288   len = MIN (len, size - 1);
 289   memcpy (buf, s, len);
 290   buf[len] = '\0';
 291 }
 292 
 293 
 294 static const char **static_shaper_list;
 295 
 296 #ifdef HB_USE_ATEXIT
 297 static
 298 void free_static_shaper_list (void)
 299 {
 300   free (static_shaper_list);
 301 }
 302 #endif
 303 
 304 /**
 305  * hb_shape_list_shapers:
 306  *
 307  * Retrieves the list of shapers supported by HarfBuzz.
 308  *
 309  * Return value: (transfer none) (array zero-terminated=1): an array of
 310  *    constant strings
 311  *
 312  * Since: 0.9.2
 313  **/
 314 const char **
 315 hb_shape_list_shapers (void)
 316 {
 317 retry:
 318   const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list);
 319 
 320   if (unlikely (!shaper_list))
 321   {
 322     /* Not found; allocate one. */
 323     shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *));
 324     if (unlikely (!shaper_list)) {
 325       static const char *nil_shaper_list[] = {NULL};
 326       return nil_shaper_list;
 327     }
 328 
 329     const hb_shaper_pair_t *shapers = _hb_shapers_get ();
 330     unsigned int i;
 331     for (i = 0; i < HB_SHAPERS_COUNT; i++)
 332       shaper_list[i] = shapers[i].name;
 333     shaper_list[i] = NULL;
 334 
 335     if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) {
 336       free (shaper_list);
 337       goto retry;
 338     }
 339 
 340 #ifdef HB_USE_ATEXIT
 341     atexit (free_static_shaper_list); /* First person registers atexit() callback. */
 342 #endif
 343   }
 344 
 345   return shaper_list;
 346 }
 347 
 348 
 349 /**
 350  * hb_shape_full:
 351  * @font: an #hb_font_t to use for shaping
 352  * @buffer: an #hb_buffer_t to shape
 353  * @features: (array length=num_features) (allow-none): an array of user
 354  *    specified #hb_feature_t or %NULL
 355  * @num_features: the length of @features array
 356  * @shaper_list: (array zero-terminated=1) (allow-none): a %NULL-terminated
 357  *    array of shapers to use or %NULL
 358  *
 359  * See hb_shape() for details. If @shaper_list is not %NULL, the specified
 360  * shapers will be used in the given order, otherwise the default shapers list
 361  * will be used.
 362  *
 363  * Return value: %FALSE if all shapers failed, %TRUE otherwise
 364  *
 365  * Since: 0.9.2
 366  **/
 367 hb_bool_t
 368 hb_shape_full (hb_font_t          *font,
 369                hb_buffer_t        *buffer,
 370                const hb_feature_t *features,
 371                unsigned int        num_features,
 372                const char * const *shaper_list)
 373 {
 374   hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list);
 375   hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features);
 376   hb_shape_plan_destroy (shape_plan);
 377 
 378   if (res)
 379     buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
 380   return res;
 381 }
 382 
 383 /**
 384  * hb_shape:
 385  * @font: an #hb_font_t to use for shaping
 386  * @buffer: an #hb_buffer_t to shape
 387  * @features: (array length=num_features) (allow-none): an array of user
 388  *    specified #hb_feature_t or %NULL
 389  * @num_features: the length of @features array
 390  *
 391  * Shapes @buffer using @font turning its Unicode characters content to
 392  * positioned glyphs. If @features is not %NULL, it will be used to control the
 393  * features applied during shaping.
 394  *
 395  * Return value: %FALSE if all shapers failed, %TRUE otherwise
 396  *
 397  * Since: 0.9.2
 398  **/
 399 void
 400 hb_shape (hb_font_t           *font,
 401           hb_buffer_t         *buffer,
 402           const hb_feature_t  *features,
 403           unsigned int         num_features)
 404 {
 405   hb_shape_full (font, buffer, features, num_features, NULL);
 406 }