Home | History | Annotate | Download | only in src
      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