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 }