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 bool
     38 parse_space (const char **pp, const char *end)
     39 {
     40   while (*pp < end && ISSPACE (**pp))
     41     (*pp)++;
     42   return true;
     43 }
     44 
     45 static bool
     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 bool
     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 bool
     82 parse_bool (const char **pp, const char *end, unsigned int *pv)
     83 {
     84   parse_space (pp, end);
     85 
     86   const char *p = *pp;
     87   while (*pp < end && ISALPHA(**pp))
     88     (*pp)++;
     89 
     90   /* CSS allows on/off as aliases 1/0. */
     91   if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
     92     *pv = 1;
     93   else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
     94     *pv = 0;
     95   else
     96     return false;
     97 
     98   return true;
     99 }
    100 
    101 static bool
    102 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
    103 {
    104   if (parse_char (pp, end, '-'))
    105     feature->value = 0;
    106   else {
    107     parse_char (pp, end, '+');
    108     feature->value = 1;
    109   }
    110 
    111   return true;
    112 }
    113 
    114 static bool
    115 parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature)
    116 {
    117   parse_space (pp, end);
    118 
    119   char quote = 0;
    120 
    121   if (*pp < end && (**pp == '\'' || **pp == '"'))
    122   {
    123     quote = **pp;
    124     (*pp)++;
    125   }
    126 
    127   const char *p = *pp;
    128   while (*pp < end && ISALNUM(**pp))
    129     (*pp)++;
    130 
    131   if (p == *pp || *pp - p > 4)
    132     return false;
    133 
    134   feature->tag = hb_tag_from_string (p, *pp - p);
    135 
    136   if (quote)
    137   {
    138     /* CSS expects exactly four bytes.  And we only allow quotations for
    139      * CSS compatibility.  So, enforce the length. */
    140      if (*pp - p != 4)
    141        return false;
    142     if (*pp == end || **pp != quote)
    143       return false;
    144     (*pp)++;
    145   }
    146 
    147   return true;
    148 }
    149 
    150 static bool
    151 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
    152 {
    153   parse_space (pp, end);
    154 
    155   bool has_start;
    156 
    157   feature->start = 0;
    158   feature->end = (unsigned int) -1;
    159 
    160   if (!parse_char (pp, end, '['))
    161     return true;
    162 
    163   has_start = parse_uint (pp, end, &feature->start);
    164 
    165   if (parse_char (pp, end, ':')) {
    166     parse_uint (pp, end, &feature->end);
    167   } else {
    168     if (has_start)
    169       feature->end = feature->start + 1;
    170   }
    171 
    172   return parse_char (pp, end, ']');
    173 }
    174 
    175 static bool
    176 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
    177 {
    178   bool had_equal = parse_char (pp, end, '=');
    179   bool had_value = parse_uint (pp, end, &feature->value) ||
    180                    parse_bool (pp, end, &feature->value);
    181   /* CSS doesn't use equal-sign between tag and value.
    182    * If there was an equal-sign, then there *must* be a value.
    183    * A value without an eqaul-sign is ok, but not required. */
    184   return !had_equal || had_value;
    185 }
    186 
    187 
    188 static bool
    189 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
    190 {
    191   return parse_feature_value_prefix (pp, end, feature) &&
    192 	 parse_feature_tag (pp, end, feature) &&
    193 	 parse_feature_indices (pp, end, feature) &&
    194 	 parse_feature_value_postfix (pp, end, feature) &&
    195 	 parse_space (pp, end) &&
    196 	 *pp == end;
    197 }
    198 
    199 /**
    200  * hb_feature_from_string:
    201  * @str: (array length=len):
    202  * @len:
    203  * @feature: (out) (optional):
    204  *
    205  *
    206  *
    207  * Return value:
    208  *
    209  * Since: 1.0
    210  **/
    211 hb_bool_t
    212 hb_feature_from_string (const char *str, int len,
    213 			hb_feature_t *feature)
    214 {
    215   hb_feature_t feat;
    216 
    217   if (len < 0)
    218     len = strlen (str);
    219 
    220   if (likely (parse_one_feature (&str, str + len, &feat)))
    221   {
    222     if (feature)
    223       *feature = feat;
    224     return true;
    225   }
    226 
    227   if (feature)
    228     memset (feature, 0, sizeof (*feature));
    229   return false;
    230 }
    231 
    232 /**
    233  * hb_feature_to_string:
    234  * @feature:
    235  * @buf: (array length=size):
    236  * @size:
    237  *
    238  *
    239  *
    240  * Since: 1.0
    241  **/
    242 void
    243 hb_feature_to_string (hb_feature_t *feature,
    244 		      char *buf, unsigned int size)
    245 {
    246   if (unlikely (!size)) return;
    247 
    248   char s[128];
    249   unsigned int len = 0;
    250   if (feature->value == 0)
    251     s[len++] = '-';
    252   hb_tag_to_string (feature->tag, s + len);
    253   len += 4;
    254   while (len && s[len - 1] == ' ')
    255     len--;
    256   if (feature->start != 0 || feature->end != (unsigned int) -1)
    257   {
    258     s[len++] = '[';
    259     if (feature->start)
    260       len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
    261     if (feature->end != feature->start + 1) {
    262       s[len++] = ':';
    263       if (feature->end != (unsigned int) -1)
    264 	len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
    265     }
    266     s[len++] = ']';
    267   }
    268   if (feature->value > 1)
    269   {
    270     s[len++] = '=';
    271     len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
    272   }
    273   assert (len < ARRAY_LENGTH (s));
    274   len = MIN (len, size - 1);
    275   memcpy (buf, s, len);
    276   buf[len] = '\0';
    277 }
    278 
    279 
    280 static const char **static_shaper_list;
    281 
    282 #ifdef HB_USE_ATEXIT
    283 static
    284 void free_static_shaper_list (void)
    285 {
    286   free (static_shaper_list);
    287 }
    288 #endif
    289 
    290 /**
    291  * hb_shape_list_shapers:
    292  *
    293  *
    294  *
    295  * Return value: (transfer none):
    296  *
    297  * Since: 1.0
    298  **/
    299 const char **
    300 hb_shape_list_shapers (void)
    301 {
    302 retry:
    303   const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list);
    304 
    305   if (unlikely (!shaper_list))
    306   {
    307     /* Not found; allocate one. */
    308     shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *));
    309     if (unlikely (!shaper_list)) {
    310       static const char *nil_shaper_list[] = {NULL};
    311       return nil_shaper_list;
    312     }
    313 
    314     const hb_shaper_pair_t *shapers = _hb_shapers_get ();
    315     unsigned int i;
    316     for (i = 0; i < HB_SHAPERS_COUNT; i++)
    317       shaper_list[i] = shapers[i].name;
    318     shaper_list[i] = NULL;
    319 
    320     if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) {
    321       free (shaper_list);
    322       goto retry;
    323     }
    324 
    325 #ifdef HB_USE_ATEXIT
    326     atexit (free_static_shaper_list); /* First person registers atexit() callback. */
    327 #endif
    328   }
    329 
    330   return shaper_list;
    331 }
    332 
    333 
    334 /**
    335  * hb_shape_full:
    336  * @font: a font.
    337  * @buffer: a buffer.
    338  * @features: (array length=num_features):
    339  * @num_features:
    340  * @shaper_list: (array zero-terminated=1):
    341  *
    342  *
    343  *
    344  * Return value:
    345  *
    346  * Since: 1.0
    347  **/
    348 hb_bool_t
    349 hb_shape_full (hb_font_t          *font,
    350 	       hb_buffer_t        *buffer,
    351 	       const hb_feature_t *features,
    352 	       unsigned int        num_features,
    353 	       const char * const *shaper_list)
    354 {
    355   if (unlikely (!buffer->len))
    356     return true;
    357 
    358   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
    359 
    360   hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list);
    361   hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features);
    362   hb_shape_plan_destroy (shape_plan);
    363 
    364   if (res)
    365     buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
    366   return res;
    367 }
    368 
    369 /**
    370  * hb_shape:
    371  * @font: a font.
    372  * @buffer: a buffer.
    373  * @features: (array length=num_features):
    374  * @num_features:
    375  *
    376  *
    377  *
    378  * Since: 1.0
    379  **/
    380 void
    381 hb_shape (hb_font_t           *font,
    382 	  hb_buffer_t         *buffer,
    383 	  const hb_feature_t  *features,
    384 	  unsigned int         num_features)
    385 {
    386   hb_shape_full (font, buffer, features, num_features, NULL);
    387 }
    388