hb-ot-shape-complex-sea.cc revision ec5448667b30ad662401c2b4f5fc0da524c013fd
1/* 2 * Copyright © 2011,2012,2013 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-complex-indic-private.hh" 28 29/* buffer var allocations */ 30#define sea_category() complex_var_u8_0() /* indic_category_t */ 31#define sea_position() complex_var_u8_1() /* indic_position_t */ 32 33 34/* 35 * South-East Asian shaper. 36 * Loosely based on the Myanmar spec / shaper. 37 * There is no OpenType spec for this. 38 */ 39 40static const hb_tag_t 41sea_features[] = 42{ 43 /* 44 * Basic features. 45 * These features are applied in order, one at a time, after initial_reordering. 46 */ 47 HB_TAG('p','r','e','f'), 48 HB_TAG('a','b','v','f'), 49 HB_TAG('b','l','w','f'), 50 HB_TAG('p','s','t','f'), 51 /* 52 * Other features. 53 * These features are applied all at once, after final_reordering. 54 */ 55 HB_TAG('p','r','e','s'), 56 HB_TAG('a','b','v','s'), 57 HB_TAG('b','l','w','s'), 58 HB_TAG('p','s','t','s'), 59 /* Positioning features, though we don't care about the types. */ 60 HB_TAG('d','i','s','t'), 61}; 62 63/* 64 * Must be in the same order as the sea_features array. 65 */ 66enum { 67 _PREF, 68 _ABVF, 69 _BLWF, 70 _PSTF, 71 72 _PRES, 73 _ABVS, 74 _BLWS, 75 _PSTS, 76 _DIST, 77 78 SEA_NUM_FEATURES, 79 SEA_BASIC_FEATURES = _PRES /* Don't forget to update this! */ 80}; 81 82static void 83setup_syllables (const hb_ot_shape_plan_t *plan, 84 hb_font_t *font, 85 hb_buffer_t *buffer); 86static void 87initial_reordering (const hb_ot_shape_plan_t *plan, 88 hb_font_t *font, 89 hb_buffer_t *buffer); 90static void 91final_reordering (const hb_ot_shape_plan_t *plan, 92 hb_font_t *font, 93 hb_buffer_t *buffer); 94 95static void 96collect_features_sea (hb_ot_shape_planner_t *plan) 97{ 98 hb_ot_map_builder_t *map = &plan->map; 99 100 /* Do this before any lookups have been applied. */ 101 map->add_gsub_pause (setup_syllables); 102 103 map->add_global_bool_feature (HB_TAG('l','o','c','l')); 104 /* The Indic specs do not require ccmp, but we apply it here since if 105 * there is a use of it, it's typically at the beginning. */ 106 map->add_global_bool_feature (HB_TAG('c','c','m','p')); 107 108 109 unsigned int i = 0; 110 map->add_gsub_pause (initial_reordering); 111 for (; i < SEA_BASIC_FEATURES; i++) { 112 map->add_global_bool_feature (sea_features[i]); 113 map->add_gsub_pause (NULL); 114 } 115 map->add_gsub_pause (final_reordering); 116 for (; i < SEA_NUM_FEATURES; i++) { 117 map->add_global_bool_feature (sea_features[i]); 118 } 119} 120 121static void 122override_features_sea (hb_ot_shape_planner_t *plan) 123{ 124 plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL); 125} 126 127 128enum syllable_type_t { 129 consonant_syllable, 130 broken_cluster, 131 non_sea_cluster, 132}; 133 134#include "hb-ot-shape-complex-sea-machine.hh" 135 136 137/* Note: This enum is duplicated in the -machine.rl source file. 138 * Not sure how to avoid duplication. */ 139enum sea_category_t { 140// OT_C = 1, 141 OT_GB = 12, /* Generic Base XXX DOTTED CIRCLE only for now */ 142// OT_H = 4, /* Halant */ 143 OT_IV = 2, /* Independent Vowel */ 144 OT_MR = 22, /* Medial Ra */ 145// OT_CM = 17, /* Consonant Medial */ 146 OT_VAbv = 26, 147 OT_VBlw = 27, 148 OT_VPre = 28, 149 OT_VPst = 29, 150 OT_T = 3, /* Tone Marks */ 151// OT_A = 10, /* Anusvara */ 152}; 153 154static inline void 155set_sea_properties (hb_glyph_info_t &info) 156{ 157 hb_codepoint_t u = info.codepoint; 158 unsigned int type = hb_indic_get_categories (u); 159 indic_category_t cat = (indic_category_t) (type & 0x7F); 160 indic_position_t pos = (indic_position_t) (type >> 8); 161 162 /* Medial Ra */ 163 if (u == 0x1A55 || u == 0xAA34) 164 cat = (indic_category_t) OT_MR; 165 166 if (cat == OT_M) 167 { 168 switch ((int) pos) 169 { 170 case POS_PRE_C: cat = (indic_category_t) OT_VPre; break; 171 case POS_ABOVE_C: cat = (indic_category_t) OT_VAbv; break; 172 case POS_BELOW_C: cat = (indic_category_t) OT_VBlw; break; 173 case POS_POST_C: cat = (indic_category_t) OT_VPst; break; 174 } 175 } 176 177 info.sea_category() = (sea_category_t) cat; 178 info.sea_position() = pos; 179} 180 181 182static void 183setup_masks_sea (const hb_ot_shape_plan_t *plan HB_UNUSED, 184 hb_buffer_t *buffer, 185 hb_font_t *font HB_UNUSED) 186{ 187 HB_BUFFER_ALLOCATE_VAR (buffer, sea_category); 188 HB_BUFFER_ALLOCATE_VAR (buffer, sea_position); 189 190 /* We cannot setup masks here. We save information about characters 191 * and setup masks later on in a pause-callback. */ 192 193 unsigned int count = buffer->len; 194 for (unsigned int i = 0; i < count; i++) 195 set_sea_properties (buffer->info[i]); 196} 197 198static void 199setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, 200 hb_font_t *font HB_UNUSED, 201 hb_buffer_t *buffer) 202{ 203 find_syllables (buffer); 204} 205 206static int 207compare_sea_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) 208{ 209 int a = pa->sea_position(); 210 int b = pb->sea_position(); 211 212 return a < b ? -1 : a == b ? 0 : +1; 213} 214 215 216static void 217initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, 218 hb_face_t *face, 219 hb_buffer_t *buffer, 220 unsigned int start, unsigned int end) 221{ 222 hb_glyph_info_t *info = buffer->info; 223 unsigned int base = start; 224 225 /* Reorder! */ 226 unsigned int i = start; 227 for (; i < base; i++) 228 info[i].sea_position() = POS_PRE_C; 229 if (i < end) 230 { 231 info[i].sea_position() = POS_BASE_C; 232 i++; 233 } 234 for (; i < end; i++) 235 { 236 if (info[i].sea_category() == OT_MR) /* Pre-base reordering */ 237 { 238 info[i].sea_position() = POS_PRE_C; 239 continue; 240 } 241 if (info[i].sea_position() < POS_BASE_C) /* Left matra */ 242 { 243 continue; 244 } 245 246 info[i].sea_position() = POS_AFTER_MAIN; 247 } 248 249 buffer->merge_clusters (start, end); 250 /* Sit tight, rock 'n roll! */ 251 hb_bubble_sort (info + start, end - start, compare_sea_order); 252} 253 254static void 255initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan, 256 hb_face_t *face, 257 hb_buffer_t *buffer, 258 unsigned int start, unsigned int end) 259{ 260 /* We already inserted dotted-circles, so just call the consonant_syllable. */ 261 initial_reordering_consonant_syllable (plan, face, buffer, start, end); 262} 263 264static void 265initial_reordering_non_sea_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED, 266 hb_face_t *face HB_UNUSED, 267 hb_buffer_t *buffer HB_UNUSED, 268 unsigned int start HB_UNUSED, unsigned int end HB_UNUSED) 269{ 270 /* Nothing to do right now. If we ever switch to using the output 271 * buffer in the reordering process, we'd need to next_glyph() here. */ 272} 273 274 275static void 276initial_reordering_syllable (const hb_ot_shape_plan_t *plan, 277 hb_face_t *face, 278 hb_buffer_t *buffer, 279 unsigned int start, unsigned int end) 280{ 281 syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); 282 switch (syllable_type) { 283 case consonant_syllable: initial_reordering_consonant_syllable (plan, face, buffer, start, end); return; 284 case broken_cluster: initial_reordering_broken_cluster (plan, face, buffer, start, end); return; 285 case non_sea_cluster: initial_reordering_non_sea_cluster (plan, face, buffer, start, end); return; 286 } 287} 288 289static inline void 290insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, 291 hb_font_t *font, 292 hb_buffer_t *buffer) 293{ 294 /* Note: This loop is extra overhead, but should not be measurable. */ 295 bool has_broken_syllables = false; 296 unsigned int count = buffer->len; 297 for (unsigned int i = 0; i < count; i++) 298 if ((buffer->info[i].syllable() & 0x0F) == broken_cluster) { 299 has_broken_syllables = true; 300 break; 301 } 302 if (likely (!has_broken_syllables)) 303 return; 304 305 306 hb_codepoint_t dottedcircle_glyph; 307 if (!font->get_glyph (0x25CC, 0, &dottedcircle_glyph)) 308 return; 309 310 hb_glyph_info_t dottedcircle = {0}; 311 dottedcircle.codepoint = 0x25CC; 312 set_sea_properties (dottedcircle); 313 dottedcircle.codepoint = dottedcircle_glyph; 314 315 buffer->clear_output (); 316 317 buffer->idx = 0; 318 unsigned int last_syllable = 0; 319 while (buffer->idx < buffer->len) 320 { 321 unsigned int syllable = buffer->cur().syllable(); 322 syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); 323 if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) 324 { 325 last_syllable = syllable; 326 327 hb_glyph_info_t info = dottedcircle; 328 info.cluster = buffer->cur().cluster; 329 info.mask = buffer->cur().mask; 330 info.syllable() = buffer->cur().syllable(); 331 332 buffer->output_info (info); 333 } 334 else 335 buffer->next_glyph (); 336 } 337 338 buffer->swap_buffers (); 339} 340 341static void 342initial_reordering (const hb_ot_shape_plan_t *plan, 343 hb_font_t *font, 344 hb_buffer_t *buffer) 345{ 346 insert_dotted_circles (plan, font, buffer); 347 348 hb_glyph_info_t *info = buffer->info; 349 unsigned int count = buffer->len; 350 if (unlikely (!count)) return; 351 unsigned int last = 0; 352 unsigned int last_syllable = info[0].syllable(); 353 for (unsigned int i = 1; i < count; i++) 354 if (last_syllable != info[i].syllable()) { 355 initial_reordering_syllable (plan, font->face, buffer, last, i); 356 last = i; 357 last_syllable = info[last].syllable(); 358 } 359 initial_reordering_syllable (plan, font->face, buffer, last, count); 360} 361 362static void 363final_reordering (const hb_ot_shape_plan_t *plan, 364 hb_font_t *font HB_UNUSED, 365 hb_buffer_t *buffer) 366{ 367 hb_glyph_info_t *info = buffer->info; 368 unsigned int count = buffer->len; 369 370 /* Zero syllables now... */ 371 for (unsigned int i = 0; i < count; i++) 372 info[i].syllable() = 0; 373 374 HB_BUFFER_DEALLOCATE_VAR (buffer, sea_category); 375 HB_BUFFER_DEALLOCATE_VAR (buffer, sea_position); 376} 377 378 379static hb_ot_shape_normalization_mode_t 380normalization_preference_sea (const hb_segment_properties_t *props HB_UNUSED) 381{ 382 return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT; 383} 384 385 386const hb_ot_complex_shaper_t _hb_ot_complex_shaper_sea = 387{ 388 "sea", 389 collect_features_sea, 390 override_features_sea, 391 NULL, /* data_create */ 392 NULL, /* data_destroy */ 393 NULL, /* preprocess_text */ 394 normalization_preference_sea, 395 NULL, /* decompose */ 396 NULL, /* compose */ 397 setup_masks_sea, 398 HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, 399 false, /* fallback_position */ 400}; 401