hb-ot-shape.cc revision 6bd9b479b8b2befbb0847282e93beade197c8038
1/* 2 * Copyright © 2009,2010 Red Hat, Inc. 3 * Copyright © 2010,2011 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-ot-shape-private.hh" 30#include "hb-ot-shape-complex-private.hh" 31 32#include "hb-font-private.hh" 33 34 35 36hb_tag_t common_features[] = { 37 HB_TAG('c','c','m','p'), 38 HB_TAG('l','o','c','l'), 39 HB_TAG('m','a','r','k'), 40 HB_TAG('m','k','m','k'), 41 HB_TAG('r','l','i','g'), 42}; 43 44hb_tag_t horizontal_features[] = { 45 HB_TAG('c','a','l','t'), 46 HB_TAG('c','l','i','g'), 47 HB_TAG('c','u','r','s'), 48 HB_TAG('k','e','r','n'), 49 HB_TAG('l','i','g','a'), 50}; 51 52/* Note: 53 * Technically speaking, vrt2 and vert are mutually exclusive. 54 * According to the spec, valt and vpal are also mutually exclusive. 55 * But we apply them all for now. 56 */ 57hb_tag_t vertical_features[] = { 58 HB_TAG('v','a','l','t'), 59 HB_TAG('v','e','r','t'), 60 HB_TAG('v','k','r','n'), 61 HB_TAG('v','p','a','l'), 62 HB_TAG('v','r','t','2'), 63}; 64 65static void 66hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, 67 const hb_segment_properties_t *props, 68 const hb_feature_t *user_features, 69 unsigned int num_user_features) 70{ 71 switch (props->direction) { 72 case HB_DIRECTION_LTR: 73 planner->map.add_bool_feature (HB_TAG ('l','t','r','a')); 74 planner->map.add_bool_feature (HB_TAG ('l','t','r','m')); 75 break; 76 case HB_DIRECTION_RTL: 77 planner->map.add_bool_feature (HB_TAG ('r','t','l','a')); 78 planner->map.add_bool_feature (HB_TAG ('r','t','l','m'), false); 79 break; 80 case HB_DIRECTION_TTB: 81 case HB_DIRECTION_BTT: 82 case HB_DIRECTION_INVALID: 83 default: 84 break; 85 } 86 87#define ADD_FEATURES(array) \ 88 HB_STMT_START { \ 89 for (unsigned int i = 0; i < ARRAY_LENGTH (array); i++) \ 90 planner->map.add_bool_feature (array[i]); \ 91 } HB_STMT_END 92 93 hb_ot_shape_complex_collect_features (planner->shaper, &planner->map, props); 94 95 ADD_FEATURES (common_features); 96 97 if (HB_DIRECTION_IS_HORIZONTAL (props->direction)) 98 ADD_FEATURES (horizontal_features); 99 else 100 ADD_FEATURES (vertical_features); 101 102#undef ADD_FEATURES 103 104 for (unsigned int i = 0; i < num_user_features; i++) { 105 const hb_feature_t *feature = &user_features[i]; 106 planner->map.add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1)); 107 } 108} 109 110 111static void 112hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) 113{ 114 hb_mask_t global_mask = c->plan->map.get_global_mask (); 115 c->buffer->reset_masks (global_mask); 116 117 hb_ot_shape_complex_setup_masks (c->plan->shaper, &c->plan->map, c->buffer, c->font); 118 119 for (unsigned int i = 0; i < c->num_user_features; i++) 120 { 121 const hb_feature_t *feature = &c->user_features[i]; 122 if (!(feature->start == 0 && feature->end == (unsigned int)-1)) { 123 unsigned int shift; 124 hb_mask_t mask = c->plan->map.get_mask (feature->tag, &shift); 125 c->buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); 126 } 127 } 128} 129 130 131/* Main shaper */ 132 133/* Prepare */ 134 135void 136_hb_set_unicode_props (hb_buffer_t *buffer) 137{ 138 unsigned int count = buffer->len; 139 for (unsigned int i = 0; i < count; i++) 140 hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode); 141} 142 143static void 144hb_form_clusters (hb_buffer_t *buffer) 145{ 146 unsigned int count = buffer->len; 147 for (unsigned int i = 1; i < count; i++) 148 if (FLAG (buffer->info[i].general_category()) & 149 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | 150 FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | 151 FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) 152 buffer->info[i].cluster = buffer->info[i - 1].cluster; 153} 154 155static void 156hb_ensure_native_direction (hb_buffer_t *buffer) 157{ 158 hb_direction_t direction = buffer->props.direction; 159 160 /* TODO vertical: 161 * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType 162 * Ogham fonts are supposed to be implemented BTT or not. Need to research that 163 * first. */ 164 if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (buffer->props.script)) || 165 (HB_DIRECTION_IS_VERTICAL (direction) && direction != HB_DIRECTION_TTB)) 166 { 167 hb_buffer_reverse_clusters (buffer); 168 buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); 169 } 170} 171 172 173/* Substitute */ 174 175static void 176hb_mirror_chars (hb_ot_shape_context_t *c) 177{ 178 hb_unicode_funcs_t *unicode = c->buffer->unicode; 179 180 if (HB_DIRECTION_IS_FORWARD (c->target_direction)) 181 return; 182 183 hb_mask_t rtlm_mask = c->plan->map.get_1_mask (HB_TAG ('r','t','l','m')); 184 185 unsigned int count = c->buffer->len; 186 for (unsigned int i = 0; i < count; i++) { 187 hb_codepoint_t codepoint = hb_unicode_mirroring (unicode, c->buffer->info[i].codepoint); 188 if (likely (codepoint == c->buffer->info[i].codepoint)) 189 c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */ 190 else 191 c->buffer->info[i].codepoint = codepoint; 192 } 193} 194 195static void 196hb_map_glyphs (hb_font_t *font, 197 hb_buffer_t *buffer) 198{ 199 hb_codepoint_t glyph; 200 201 if (unlikely (!buffer->len)) 202 return; 203 204 buffer->clear_output (); 205 206 unsigned int count = buffer->len - 1; 207 for (buffer->idx = 0; buffer->idx < count;) { 208 if (unlikely (_hb_unicode_is_variation_selector (buffer->info[buffer->idx + 1].codepoint))) { 209 hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, buffer->info[buffer->idx + 1].codepoint, &glyph); 210 buffer->replace_glyph (glyph); 211 buffer->skip_glyph (); 212 } else { 213 hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph); 214 buffer->replace_glyph (glyph); 215 } 216 } 217 if (likely (buffer->idx < buffer->len)) { 218 hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph); 219 buffer->replace_glyph (glyph); 220 } 221 buffer->swap_buffers (); 222} 223 224static void 225hb_substitute_default (hb_ot_shape_context_t *c) 226{ 227 hb_ot_layout_substitute_start (c->buffer); 228 229 hb_mirror_chars (c); 230 231 hb_map_glyphs (c->font, c->buffer); 232} 233 234static void 235hb_ot_substitute_complex (hb_ot_shape_context_t *c) 236{ 237 if (hb_ot_layout_has_substitution (c->face)) { 238 c->plan->map.substitute (c->face, c->buffer); 239 } 240 241 hb_ot_layout_substitute_finish (c->buffer); 242 243 return; 244} 245 246 247/* Position */ 248 249static void 250hb_position_default (hb_ot_shape_context_t *c) 251{ 252 hb_ot_layout_position_start (c->buffer); 253 254 unsigned int count = c->buffer->len; 255 for (unsigned int i = 0; i < count; i++) { 256 hb_font_get_glyph_advance_for_direction (c->font, c->buffer->info[i].codepoint, 257 c->buffer->props.direction, 258 &c->buffer->pos[i].x_advance, 259 &c->buffer->pos[i].y_advance); 260 hb_font_subtract_glyph_origin_for_direction (c->font, c->buffer->info[i].codepoint, 261 c->buffer->props.direction, 262 &c->buffer->pos[i].x_offset, 263 &c->buffer->pos[i].y_offset); 264 } 265} 266 267static void 268hb_ot_position_complex (hb_ot_shape_context_t *c) 269{ 270 271 if (hb_ot_layout_has_positioning (c->face)) 272 { 273 /* Change glyph origin to what GPOS expects, apply GPOS, change it back. */ 274 275 unsigned int count = c->buffer->len; 276 for (unsigned int i = 0; i < count; i++) { 277 hb_font_add_glyph_origin_for_direction (c->font, c->buffer->info[i].codepoint, 278 HB_DIRECTION_LTR, 279 &c->buffer->pos[i].x_offset, 280 &c->buffer->pos[i].y_offset); 281 } 282 283 c->plan->map.position (c->font, c->buffer); 284 285 for (unsigned int i = 0; i < count; i++) { 286 hb_font_subtract_glyph_origin_for_direction (c->font, c->buffer->info[i].codepoint, 287 HB_DIRECTION_LTR, 288 &c->buffer->pos[i].x_offset, 289 &c->buffer->pos[i].y_offset); 290 } 291 292 c->applied_position_complex = TRUE; 293 } 294 295 hb_ot_layout_position_finish (c->buffer); 296 297 return; 298} 299 300static void 301hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED) 302{ 303 /* TODO Mark pos */ 304} 305 306static void 307hb_truetype_kern (hb_ot_shape_context_t *c) 308{ 309 /* TODO Check for kern=0 */ 310 unsigned int count = c->buffer->len; 311 for (unsigned int i = 1; i < count; i++) { 312 hb_position_t x_kern, y_kern, kern1, kern2; 313 hb_font_get_glyph_kerning_for_direction (c->font, 314 c->buffer->info[i - 1].codepoint, c->buffer->info[i].codepoint, 315 c->buffer->props.direction, 316 &x_kern, &y_kern); 317 318 kern1 = x_kern >> 1; 319 kern2 = x_kern - kern1; 320 c->buffer->pos[i - 1].x_advance += kern1; 321 c->buffer->pos[i].x_advance += kern2; 322 c->buffer->pos[i].x_offset += kern2; 323 324 kern1 = y_kern >> 1; 325 kern2 = y_kern - kern1; 326 c->buffer->pos[i - 1].y_advance += kern1; 327 c->buffer->pos[i].y_advance += kern2; 328 c->buffer->pos[i].y_offset += kern2; 329 } 330} 331 332static void 333hb_position_complex_fallback_visual (hb_ot_shape_context_t *c) 334{ 335 hb_truetype_kern (c); 336} 337 338 339/* Do it! */ 340 341static void 342hb_ot_shape_execute_internal (hb_ot_shape_context_t *c) 343{ 344 c->buffer->deallocate_var_all (); 345 346 /* Save the original direction, we use it later. */ 347 c->target_direction = c->buffer->props.direction; 348 349 HB_BUFFER_ALLOCATE_VAR (c->buffer, general_category); 350 HB_BUFFER_ALLOCATE_VAR (c->buffer, combining_class); 351 352 _hb_set_unicode_props (c->buffer); /* BUFFER: Set general_category and combining_class */ 353 354 hb_form_clusters (c->buffer); 355 356 hb_ensure_native_direction (c->buffer); 357 358 _hb_ot_shape_normalize (c->font, c->buffer, hb_ot_shape_complex_normalization_preference (c->plan->shaper)); 359 360 hb_ot_shape_setup_masks (c); 361 362 /* SUBSTITUTE */ 363 { 364 hb_substitute_default (c); 365 366 hb_ot_substitute_complex (c); 367 } 368 369 /* POSITION */ 370 { 371 hb_position_default (c); 372 373 hb_ot_position_complex (c); 374 375 hb_bool_t position_fallback = !c->applied_position_complex; 376 if (position_fallback) 377 hb_position_complex_fallback (c); 378 379 if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) 380 hb_buffer_reverse (c->buffer); 381 382 if (position_fallback) 383 hb_position_complex_fallback_visual (c); 384 } 385 386 HB_BUFFER_DEALLOCATE_VAR (c->buffer, combining_class); 387 HB_BUFFER_DEALLOCATE_VAR (c->buffer, general_category); 388 389 c->buffer->props.direction = c->target_direction; 390 391 c->buffer->deallocate_var_all (); 392} 393 394static void 395hb_ot_shape_plan_internal (hb_ot_shape_plan_t *plan, 396 hb_face_t *face, 397 const hb_segment_properties_t *props, 398 const hb_feature_t *user_features, 399 unsigned int num_user_features) 400{ 401 hb_ot_shape_planner_t planner; 402 403 planner.shaper = hb_ot_shape_complex_categorize (props); 404 405 hb_ot_shape_collect_features (&planner, props, user_features, num_user_features); 406 407 planner.compile (face, props, *plan); 408} 409 410static void 411hb_ot_shape_execute (hb_ot_shape_plan_t *plan, 412 hb_font_t *font, 413 hb_buffer_t *buffer, 414 const hb_feature_t *user_features, 415 unsigned int num_user_features) 416{ 417 hb_ot_shape_context_t c = {plan, font, font->face, buffer, user_features, num_user_features}; 418 hb_ot_shape_execute_internal (&c); 419} 420 421hb_bool_t 422_hb_ot_shape (hb_font_t *font, 423 hb_buffer_t *buffer, 424 const hb_feature_t *features, 425 unsigned int num_features) 426{ 427 hb_ot_shape_plan_t plan; 428 429 buffer->guess_properties (); 430 431 hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features); 432 hb_ot_shape_execute (&plan, font, buffer, features, num_features); 433 434 return TRUE; 435} 436