Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright  2011,2012  Google, Inc.
      3  *
      4  *  This is part of HarfBuzz, a text shaping library.
      5  *
      6  * Permission is hereby granted, without written agreement and without
      7  * license or royalty fees, to use, copy, modify, and distribute this
      8  * software and its documentation for any purpose, provided that the
      9  * above copyright notice and the following two paragraphs appear in
     10  * all copies of this software.
     11  *
     12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
     13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
     14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
     15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
     16  * DAMAGE.
     17  *
     18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
     19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
     21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
     22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     23  *
     24  * Google Author(s): Behdad Esfahbod
     25  */
     26 
     27 #include "hb-ot-shape-fallback-private.hh"
     28 #include "hb-ot-layout-gsubgpos-private.hh"
     29 
     30 static unsigned int
     31 recategorize_combining_class (hb_codepoint_t u,
     32 			      unsigned int klass)
     33 {
     34   if (klass >= 200)
     35     return klass;
     36 
     37   /* Thai / Lao need some per-character work. */
     38   if ((u & ~0xFF) == 0x0E00u)
     39   {
     40     if (unlikely (klass == 0))
     41     {
     42       switch (u)
     43       {
     44         case 0x0E31u:
     45         case 0x0E34u:
     46         case 0x0E35u:
     47         case 0x0E36u:
     48         case 0x0E37u:
     49         case 0x0E47u:
     50         case 0x0E4Cu:
     51         case 0x0E4Du:
     52         case 0x0E4Eu:
     53 	  klass = HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT;
     54 	  break;
     55 
     56         case 0x0EB1u:
     57         case 0x0EB4u:
     58         case 0x0EB5u:
     59         case 0x0EB6u:
     60         case 0x0EB7u:
     61         case 0x0EBBu:
     62         case 0x0ECCu:
     63         case 0x0ECDu:
     64 	  klass = HB_UNICODE_COMBINING_CLASS_ABOVE;
     65 	  break;
     66 
     67         case 0x0EBCu:
     68 	  klass = HB_UNICODE_COMBINING_CLASS_BELOW;
     69 	  break;
     70       }
     71     } else {
     72       /* Thai virama is below-right */
     73       if (u == 0x0E3Au)
     74 	klass = HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT;
     75     }
     76   }
     77 
     78   switch (klass)
     79   {
     80 
     81     /* Hebrew */
     82 
     83     case HB_MODIFIED_COMBINING_CLASS_CCC10: /* sheva */
     84     case HB_MODIFIED_COMBINING_CLASS_CCC11: /* hataf segol */
     85     case HB_MODIFIED_COMBINING_CLASS_CCC12: /* hataf patah */
     86     case HB_MODIFIED_COMBINING_CLASS_CCC13: /* hataf qamats */
     87     case HB_MODIFIED_COMBINING_CLASS_CCC14: /* hiriq */
     88     case HB_MODIFIED_COMBINING_CLASS_CCC15: /* tsere */
     89     case HB_MODIFIED_COMBINING_CLASS_CCC16: /* segol */
     90     case HB_MODIFIED_COMBINING_CLASS_CCC17: /* patah */
     91     case HB_MODIFIED_COMBINING_CLASS_CCC18: /* qamats */
     92     case HB_MODIFIED_COMBINING_CLASS_CCC20: /* qubuts */
     93     case HB_MODIFIED_COMBINING_CLASS_CCC22: /* meteg */
     94       return HB_UNICODE_COMBINING_CLASS_BELOW;
     95 
     96     case HB_MODIFIED_COMBINING_CLASS_CCC23: /* rafe */
     97       return HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE;
     98 
     99     case HB_MODIFIED_COMBINING_CLASS_CCC24: /* shin dot */
    100       return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT;
    101 
    102     case HB_MODIFIED_COMBINING_CLASS_CCC25: /* sin dot */
    103     case HB_MODIFIED_COMBINING_CLASS_CCC19: /* holam */
    104       return HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT;
    105 
    106     case HB_MODIFIED_COMBINING_CLASS_CCC26: /* point varika */
    107       return HB_UNICODE_COMBINING_CLASS_ABOVE;
    108 
    109     case HB_MODIFIED_COMBINING_CLASS_CCC21: /* dagesh */
    110       break;
    111 
    112 
    113     /* Arabic and Syriac */
    114 
    115     case HB_MODIFIED_COMBINING_CLASS_CCC27: /* fathatan */
    116     case HB_MODIFIED_COMBINING_CLASS_CCC28: /* dammatan */
    117     case HB_MODIFIED_COMBINING_CLASS_CCC30: /* fatha */
    118     case HB_MODIFIED_COMBINING_CLASS_CCC31: /* damma */
    119     case HB_MODIFIED_COMBINING_CLASS_CCC33: /* shadda */
    120     case HB_MODIFIED_COMBINING_CLASS_CCC34: /* sukun */
    121     case HB_MODIFIED_COMBINING_CLASS_CCC35: /* superscript alef */
    122     case HB_MODIFIED_COMBINING_CLASS_CCC36: /* superscript alaph */
    123       return HB_UNICODE_COMBINING_CLASS_ABOVE;
    124 
    125     case HB_MODIFIED_COMBINING_CLASS_CCC29: /* kasratan */
    126     case HB_MODIFIED_COMBINING_CLASS_CCC32: /* kasra */
    127       return HB_UNICODE_COMBINING_CLASS_BELOW;
    128 
    129 
    130     /* Thai */
    131 
    132     case HB_MODIFIED_COMBINING_CLASS_CCC103: /* sara u / sara uu */
    133       return HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT;
    134 
    135     case HB_MODIFIED_COMBINING_CLASS_CCC107: /* mai */
    136       return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT;
    137 
    138 
    139     /* Lao */
    140 
    141     case HB_MODIFIED_COMBINING_CLASS_CCC118: /* sign u / sign uu */
    142       return HB_UNICODE_COMBINING_CLASS_BELOW;
    143 
    144     case HB_MODIFIED_COMBINING_CLASS_CCC122: /* mai */
    145       return HB_UNICODE_COMBINING_CLASS_ABOVE;
    146 
    147 
    148     /* Tibetan */
    149 
    150     case HB_MODIFIED_COMBINING_CLASS_CCC129: /* sign aa */
    151       return HB_UNICODE_COMBINING_CLASS_BELOW;
    152 
    153     case HB_MODIFIED_COMBINING_CLASS_CCC130: /* sign i*/
    154       return HB_UNICODE_COMBINING_CLASS_ABOVE;
    155 
    156     case HB_MODIFIED_COMBINING_CLASS_CCC132: /* sign u */
    157       return HB_UNICODE_COMBINING_CLASS_BELOW;
    158 
    159   }
    160 
    161   return klass;
    162 }
    163 
    164 void
    165 _hb_ot_shape_fallback_position_recategorize_marks (const hb_ot_shape_plan_t *plan HB_UNUSED,
    166 						   hb_font_t *font HB_UNUSED,
    167 						   hb_buffer_t  *buffer)
    168 {
    169   unsigned int count = buffer->len;
    170   hb_glyph_info_t *info = buffer->info;
    171   for (unsigned int i = 0; i < count; i++)
    172     if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
    173       unsigned int combining_class = _hb_glyph_info_get_modified_combining_class (&info[i]);
    174       combining_class = recategorize_combining_class (info[i].codepoint, combining_class);
    175       _hb_glyph_info_set_modified_combining_class (&info[i], combining_class);
    176     }
    177 }
    178 
    179 
    180 static void
    181 zero_mark_advances (hb_buffer_t *buffer,
    182 		    unsigned int start,
    183 		    unsigned int end)
    184 {
    185   hb_glyph_info_t *info = buffer->info;
    186   for (unsigned int i = start; i < end; i++)
    187     if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
    188     {
    189       buffer->pos[i].x_advance = 0;
    190       buffer->pos[i].y_advance = 0;
    191     }
    192 }
    193 
    194 static inline void
    195 position_mark (const hb_ot_shape_plan_t *plan,
    196 	       hb_font_t *font,
    197 	       hb_buffer_t  *buffer,
    198 	       hb_glyph_extents_t &base_extents,
    199 	       unsigned int i,
    200 	       unsigned int combining_class)
    201 {
    202   hb_glyph_extents_t mark_extents;
    203   if (!font->get_glyph_extents (buffer->info[i].codepoint,
    204 				&mark_extents))
    205     return;
    206 
    207   hb_position_t y_gap = font->y_scale / 16;
    208 
    209   hb_glyph_position_t &pos = buffer->pos[i];
    210   pos.x_offset = pos.y_offset = 0;
    211 
    212 
    213   /* We don't position LEFT and RIGHT marks. */
    214 
    215   /* X positioning */
    216   switch (combining_class)
    217   {
    218     case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW:
    219     case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE:
    220       if (buffer->props.direction == HB_DIRECTION_LTR) {
    221 	pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width / 2 - mark_extents.x_bearing;
    222         break;
    223       } else if (buffer->props.direction == HB_DIRECTION_RTL) {
    224 	pos.x_offset += base_extents.x_bearing - mark_extents.width / 2 - mark_extents.x_bearing;
    225         break;
    226       }
    227       HB_FALLTHROUGH;
    228 
    229     default:
    230     case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW:
    231     case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE:
    232     case HB_UNICODE_COMBINING_CLASS_BELOW:
    233     case HB_UNICODE_COMBINING_CLASS_ABOVE:
    234       /* Center align. */
    235       pos.x_offset += base_extents.x_bearing + (base_extents.width - mark_extents.width) / 2 - mark_extents.x_bearing;
    236       break;
    237 
    238     case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT:
    239     case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT:
    240     case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT:
    241       /* Left align. */
    242       pos.x_offset += base_extents.x_bearing - mark_extents.x_bearing;
    243       break;
    244 
    245     case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT:
    246     case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT:
    247     case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT:
    248       /* Right align. */
    249       pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width - mark_extents.x_bearing;
    250       break;
    251   }
    252 
    253   /* Y positioning */
    254   switch (combining_class)
    255   {
    256     case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW:
    257     case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT:
    258     case HB_UNICODE_COMBINING_CLASS_BELOW:
    259     case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT:
    260       /* Add gap, fall-through. */
    261       base_extents.height -= y_gap;
    262       HB_FALLTHROUGH;
    263 
    264     case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT:
    265     case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW:
    266       pos.y_offset = base_extents.y_bearing + base_extents.height - mark_extents.y_bearing;
    267       /* Never shift up "below" marks. */
    268       if ((y_gap > 0) == (pos.y_offset > 0))
    269       {
    270 	base_extents.height -= pos.y_offset;
    271 	pos.y_offset = 0;
    272       }
    273       base_extents.height += mark_extents.height;
    274       break;
    275 
    276     case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE:
    277     case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT:
    278     case HB_UNICODE_COMBINING_CLASS_ABOVE:
    279     case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT:
    280       /* Add gap, fall-through. */
    281       base_extents.y_bearing += y_gap;
    282       base_extents.height -= y_gap;
    283       HB_FALLTHROUGH;
    284 
    285     case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE:
    286     case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT:
    287       pos.y_offset = base_extents.y_bearing - (mark_extents.y_bearing + mark_extents.height);
    288       /* Don't shift down "above" marks too much. */
    289       if ((y_gap > 0) != (pos.y_offset > 0))
    290       {
    291 	unsigned int correction = -pos.y_offset / 2;
    292 	base_extents.y_bearing += correction;
    293 	base_extents.height -= correction;
    294 	pos.y_offset += correction;
    295       }
    296       base_extents.y_bearing -= mark_extents.height;
    297       base_extents.height += mark_extents.height;
    298       break;
    299   }
    300 }
    301 
    302 static inline void
    303 position_around_base (const hb_ot_shape_plan_t *plan,
    304 		      hb_font_t *font,
    305 		      hb_buffer_t  *buffer,
    306 		      unsigned int base,
    307 		      unsigned int end)
    308 {
    309   hb_direction_t horiz_dir = HB_DIRECTION_INVALID;
    310 
    311   buffer->unsafe_to_break (base, end);
    312 
    313   hb_glyph_extents_t base_extents;
    314   if (!font->get_glyph_extents (buffer->info[base].codepoint,
    315 				&base_extents))
    316   {
    317     /* If extents don't work, zero marks and go home. */
    318     zero_mark_advances (buffer, base + 1, end);
    319     return;
    320   }
    321   base_extents.x_bearing += buffer->pos[base].x_offset;
    322   base_extents.y_bearing += buffer->pos[base].y_offset;
    323 
    324   unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[base]);
    325   unsigned int num_lig_components = _hb_glyph_info_get_lig_num_comps (&buffer->info[base]);
    326 
    327   hb_position_t x_offset = 0, y_offset = 0;
    328   if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
    329     x_offset -= buffer->pos[base].x_advance;
    330     y_offset -= buffer->pos[base].y_advance;
    331   }
    332 
    333   hb_glyph_extents_t component_extents = base_extents;
    334   unsigned int last_lig_component = (unsigned int) -1;
    335   unsigned int last_combining_class = 255;
    336   hb_glyph_extents_t cluster_extents = base_extents; /* Initialization is just to shut gcc up. */
    337   hb_glyph_info_t *info = buffer->info;
    338   for (unsigned int i = base + 1; i < end; i++)
    339     if (_hb_glyph_info_get_modified_combining_class (&info[i]))
    340     {
    341       if (num_lig_components > 1) {
    342 	unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&info[i]);
    343 	unsigned int this_lig_component = _hb_glyph_info_get_lig_comp (&info[i]) - 1;
    344 	/* Conditions for attaching to the last component. */
    345 	if (!lig_id || lig_id != this_lig_id || this_lig_component >= num_lig_components)
    346 	  this_lig_component = num_lig_components - 1;
    347 	if (last_lig_component != this_lig_component)
    348 	{
    349 	  last_lig_component = this_lig_component;
    350 	  last_combining_class = 255;
    351 	  component_extents = base_extents;
    352 	  if (unlikely (horiz_dir == HB_DIRECTION_INVALID)) {
    353 	    if (HB_DIRECTION_IS_HORIZONTAL (plan->props.direction))
    354 	      horiz_dir = plan->props.direction;
    355 	    else
    356 	      horiz_dir = hb_script_get_horizontal_direction (plan->props.script);
    357 	  }
    358 	  if (horiz_dir == HB_DIRECTION_LTR)
    359 	    component_extents.x_bearing += (this_lig_component * component_extents.width) / num_lig_components;
    360 	  else
    361 	    component_extents.x_bearing += ((num_lig_components - 1 - this_lig_component) * component_extents.width) / num_lig_components;
    362 	  component_extents.width /= num_lig_components;
    363 	}
    364       }
    365 
    366       unsigned int this_combining_class = _hb_glyph_info_get_modified_combining_class (&info[i]);
    367       if (last_combining_class != this_combining_class)
    368       {
    369 	last_combining_class = this_combining_class;
    370         cluster_extents = component_extents;
    371       }
    372 
    373       position_mark (plan, font, buffer, cluster_extents, i, this_combining_class);
    374 
    375       buffer->pos[i].x_advance = 0;
    376       buffer->pos[i].y_advance = 0;
    377       buffer->pos[i].x_offset += x_offset;
    378       buffer->pos[i].y_offset += y_offset;
    379 
    380     } else {
    381       if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
    382 	x_offset -= buffer->pos[i].x_advance;
    383 	y_offset -= buffer->pos[i].y_advance;
    384       } else {
    385 	x_offset += buffer->pos[i].x_advance;
    386 	y_offset += buffer->pos[i].y_advance;
    387       }
    388     }
    389 }
    390 
    391 static inline void
    392 position_cluster (const hb_ot_shape_plan_t *plan,
    393 		  hb_font_t *font,
    394 		  hb_buffer_t  *buffer,
    395 		  unsigned int start,
    396 		  unsigned int end)
    397 {
    398   if (end - start < 2)
    399     return;
    400 
    401   /* Find the base glyph */
    402   hb_glyph_info_t *info = buffer->info;
    403   for (unsigned int i = start; i < end; i++)
    404     if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))
    405     {
    406       /* Find mark glyphs */
    407       unsigned int j;
    408       for (j = i + 1; j < end; j++)
    409 	if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[j])))
    410 	  break;
    411 
    412       position_around_base (plan, font, buffer, i, j);
    413 
    414       i = j - 1;
    415     }
    416 }
    417 
    418 void
    419 _hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan,
    420 				hb_font_t *font,
    421 				hb_buffer_t  *buffer)
    422 {
    423   _hb_buffer_assert_gsubgpos_vars (buffer);
    424 
    425   unsigned int start = 0;
    426   unsigned int count = buffer->len;
    427   hb_glyph_info_t *info = buffer->info;
    428   for (unsigned int i = 1; i < count; i++)
    429     if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))) {
    430       position_cluster (plan, font, buffer, start, i);
    431       start = i;
    432     }
    433   position_cluster (plan, font, buffer, start, count);
    434 }
    435 
    436 
    437 /* Performs old-style TrueType kerning. */
    438 void
    439 _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
    440 			    hb_font_t *font,
    441 			    hb_buffer_t  *buffer)
    442 {
    443   if (!plan->has_kern) return;
    444 
    445   OT::hb_apply_context_t c (1, font, buffer);
    446   c.set_lookup_mask (plan->kern_mask);
    447   c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
    448   OT::hb_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input;
    449   skippy_iter.init (&c);
    450 
    451   unsigned int count = buffer->len;
    452   hb_glyph_info_t *info = buffer->info;
    453   hb_glyph_position_t *pos = buffer->pos;
    454   for (unsigned int idx = 0; idx < count;)
    455   {
    456     skippy_iter.reset (idx, 1);
    457     if (!skippy_iter.next ())
    458     {
    459       idx++;
    460       continue;
    461     }
    462 
    463     hb_position_t x_kern, y_kern;
    464     font->get_glyph_kerning_for_direction (info[idx].codepoint,
    465 					   info[skippy_iter.idx].codepoint,
    466 					   buffer->props.direction,
    467 					   &x_kern, &y_kern);
    468 
    469     if (x_kern)
    470     {
    471       hb_position_t kern1 = x_kern >> 1;
    472       hb_position_t kern2 = x_kern - kern1;
    473       pos[idx].x_advance += kern1;
    474       pos[skippy_iter.idx].x_advance += kern2;
    475       pos[skippy_iter.idx].x_offset += kern2;
    476     }
    477 
    478     if (y_kern)
    479     {
    480       hb_position_t kern1 = y_kern >> 1;
    481       hb_position_t kern2 = y_kern - kern1;
    482       pos[idx].y_advance += kern1;
    483       pos[skippy_iter.idx].y_advance += kern2;
    484       pos[skippy_iter.idx].y_offset += kern2;
    485     }
    486 
    487     idx = skippy_iter.idx;
    488   }
    489 }
    490 
    491 
    492 /* Adjusts width of various spaces. */
    493 void
    494 _hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan,
    495 			      hb_font_t *font,
    496 			      hb_buffer_t  *buffer)
    497 {
    498   if (!HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
    499     return;
    500 
    501   hb_glyph_info_t *info = buffer->info;
    502   hb_glyph_position_t *pos = buffer->pos;
    503   unsigned int count = buffer->len;
    504   for (unsigned int i = 0; i < count; i++)
    505     if (_hb_glyph_info_is_unicode_space (&info[i]) && !_hb_glyph_info_ligated (&info[i]))
    506     {
    507       hb_unicode_funcs_t::space_t space_type = _hb_glyph_info_get_unicode_space_fallback_type (&info[i]);
    508       hb_codepoint_t glyph;
    509       typedef hb_unicode_funcs_t t;
    510       switch (space_type)
    511       {
    512 	case t::NOT_SPACE: /* Shouldn't happen. */
    513 	case t::SPACE:
    514 	  break;
    515 
    516 	case t::SPACE_EM:
    517 	case t::SPACE_EM_2:
    518 	case t::SPACE_EM_3:
    519 	case t::SPACE_EM_4:
    520 	case t::SPACE_EM_5:
    521 	case t::SPACE_EM_6:
    522 	case t::SPACE_EM_16:
    523 	  pos[i].x_advance = (font->x_scale + ((int) space_type)/2) / (int) space_type;
    524 	  break;
    525 
    526 	case t::SPACE_4_EM_18:
    527 	  pos[i].x_advance = font->x_scale * 4 / 18;
    528 	  break;
    529 
    530 	case t::SPACE_FIGURE:
    531 	  for (char u = '0'; u <= '9'; u++)
    532 	    if (font->get_nominal_glyph (u, &glyph))
    533 	    {
    534 	      pos[i].x_advance = font->get_glyph_h_advance (glyph);
    535 	      break;
    536 	    }
    537 	  break;
    538 
    539 	case t::SPACE_PUNCTUATION:
    540 	  if (font->get_nominal_glyph ('.', &glyph))
    541 	    pos[i].x_advance = font->get_glyph_h_advance (glyph);
    542 	  else if (font->get_nominal_glyph (',', &glyph))
    543 	    pos[i].x_advance = font->get_glyph_h_advance (glyph);
    544 	  break;
    545 
    546 	case t::SPACE_NARROW:
    547 	  /* Half-space?
    548 	   * Unicode doc http://www.unicode.org/charts/PDF/U2000.pdf says ~1/4 or 1/5 of EM.
    549 	   * However, in my testing, many fonts have their regular space being about that
    550 	   * size.  To me, a percentage of the space width makes more sense.  Half is as
    551 	   * good as any. */
    552 	  pos[i].x_advance /= 2;
    553 	  break;
    554       }
    555     }
    556 }
    557